diff options
464 files changed, 33934 insertions, 13611 deletions
diff --git a/Android.bp b/Android.bp index 8c4dfbbea4..2520a71968 100644 --- a/Android.bp +++ b/Android.bp @@ -105,7 +105,13 @@ aidl_library { cc_library_headers { name: "libandroid_headers_private", + host_supported: true, export_include_dirs: ["include/private"], + target: { + windows: { + enabled: true, + }, + }, } filegroup { diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 4a77967bb2..a1c10f59ad 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -114,6 +114,7 @@ cc_defaults { "libincidentcompanion", "libdumpsys", "libserviceutils", + "android.tracing.flags_c_lib", ], } diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 9b453826c4..7ffef2c5e5 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -17,49 +17,9 @@ #define LOG_TAG "dumpstate" #define ATRACE_TAG ATRACE_TAG_ALWAYS -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <libgen.h> -#include <limits.h> -#include <math.h> -#include <poll.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mount.h> -#include <sys/poll.h> -#include <sys/prctl.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <signal.h> -#include <stdarg.h> -#include <string.h> -#include <sys/capability.h> -#include <sys/inotify.h> -#include <sys/klog.h> -#include <time.h> -#include <unistd.h> - -#include <chrono> -#include <cmath> -#include <fstream> -#include <functional> -#include <future> -#include <memory> -#include <numeric> -#include <regex> -#include <set> -#include <string> -#include <utility> -#include <vector> +#include "dumpstate.h" #include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> -#include <android_app_admin_flags.h> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/scopeguard.h> @@ -74,6 +34,8 @@ #include <android/hardware/dumpstate/1.1/types.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <android/os/IIncidentCompanion.h> +#include <android_app_admin_flags.h> +#include <android_tracing.h> #include <binder/IServiceManager.h> #include <cutils/multiuser.h> #include <cutils/native_handle.h> @@ -81,21 +43,60 @@ #include <cutils/sockets.h> #include <cutils/trace.h> #include <debuggerd/client.h> +#include <dirent.h> #include <dumpsys.h> #include <dumputils/dump_utils.h> +#include <errno.h> +#include <fcntl.h> #include <hardware_legacy/power.h> #include <hidl/ServiceManagement.h> +#include <inttypes.h> +#include <libgen.h> +#include <limits.h> #include <log/log.h> #include <log/log_read.h> +#include <math.h> #include <openssl/sha.h> +#include <poll.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> #include <serviceutils/PriorityDumper.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/capability.h> +#include <sys/inotify.h> +#include <sys/klog.h> +#include <sys/mount.h> +#include <sys/poll.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <time.h> +#include <unistd.h> #include <utils/StrongPointer.h> #include <vintf/VintfObject.h> + +#include <chrono> +#include <cmath> +#include <fstream> +#include <functional> +#include <future> +#include <memory> +#include <numeric> +#include <regex> +#include <set> +#include <string> +#include <utility> +#include <vector> + #include "DumpstateInternal.h" #include "DumpstateService.h" -#include "dumpstate.h" namespace dumpstate_hal_hidl_1_0 = android::hardware::dumpstate::V1_0; namespace dumpstate_hal_hidl = android::hardware::dumpstate::V1_1; @@ -248,7 +249,7 @@ static const std::string DUMP_NETSTATS_PROTO_TASK = "DUMP NETSTATS PROTO"; static const std::string DUMP_HALS_TASK = "DUMP HALS"; static const std::string DUMP_BOARD_TASK = "dumpstate_board()"; static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS"; -static const std::string POST_PROCESS_UI_TRACES_TASK = "POST-PROCESS UI TRACES"; +static const std::string SERIALIZE_PERFETTO_TRACE_TASK = "SERIALIZE PERFETTO TRACE"; namespace android { namespace os { @@ -1088,11 +1089,11 @@ static void DumpNetstatsProto() { static void MaybeAddSystemTraceToZip() { // This function copies into the .zip the system trace that was snapshotted - // by the early call to MaybeSnapshotSystemTrace(), if any background + // by the early call to MaybeSnapshotSystemTraceAsync(), if any background // tracing was happening. bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0; if (!system_trace_exists) { - // No background trace was happening at the time MaybeSnapshotSystemTrace() was invoked. + // No background trace was happening at the time MaybeSnapshotSystemTraceAsync() was invoked if (!PropertiesHelper::IsUserBuild()) { MYLOGI( "No system traces found. Check for previously uploaded traces by looking for " @@ -1564,6 +1565,13 @@ static void DumpstateLimitedOnly() { RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"}); RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"}); + + printf("========================================================\n"); + printf("== ANR Traces\n"); + printf("========================================================\n"); + + AddAnrTraceFiles(); + printf("========================================================\n"); printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(), ds.progress_->GetMax(), ds.progress_->GetInitialMax()); @@ -1647,7 +1655,7 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { // Enqueue slow functions into the thread pool, if the parallel run is enabled. std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins, - dump_netstats_report, post_process_ui_traces; + dump_netstats_report; if (ds.dump_pool_) { // Pool was shutdown in DumpstateDefaultAfterCritical method in order to // drop root user. Restarts it. @@ -3088,8 +3096,9 @@ void Dumpstate::Cancel() { } void Dumpstate::PreDumpUiData() { - MaybeSnapshotSystemTrace(); + auto snapshot_system_trace = MaybeSnapshotSystemTraceAsync(); MaybeSnapshotUiTraces(); + MaybeWaitForSnapshotSystemTrace(std::move(snapshot_system_trace)); } /* @@ -3275,13 +3284,15 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // duration is logged into MYLOG instead. PrintHeader(); + std::future<std::string> snapshot_system_trace; + bool is_dumpstate_restricted = options_->telephony_only || options_->wifi_only || options_->limited_only; if (!is_dumpstate_restricted) { // Snapshot the system trace now (if running) to avoid that dumpstate's // own activity pushes out interesting data from the trace ring buffer. // The trace file is added to the zip by MaybeAddSystemTraceToZip(). - MaybeSnapshotSystemTrace(); + snapshot_system_trace = MaybeSnapshotSystemTraceAsync(); // Invoke critical dumpsys to preserve system state, before doing anything else. RunDumpsysCritical(); @@ -3292,6 +3303,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } MaybeTakeEarlyScreenshot(); + MaybeWaitForSnapshotSystemTrace(std::move(snapshot_system_trace)); onUiIntensiveBugreportDumpsFinished(calling_uid); MaybeCheckUserConsent(calling_uid, calling_package); if (options_->telephony_only) { @@ -3388,31 +3400,59 @@ void Dumpstate::MaybeTakeEarlyScreenshot() { TakeScreenshot(); } -void Dumpstate::MaybeSnapshotSystemTrace() { +std::future<std::string> Dumpstate::MaybeSnapshotSystemTraceAsync() { // When capturing traces via bugreport handler (BH), this function will be invoked twice: // 1) When BH invokes IDumpstate::PreDumpUiData() // 2) When BH invokes IDumpstate::startBugreport(flags = BUGREPORT_USE_PREDUMPED_UI_DATA) // In this case we don't want to re-invoke perfetto in step 2. // In all other standard invocation states, this function is invoked once // without the flag BUGREPORT_USE_PREDUMPED_UI_DATA. + // This function must run asynchronously to avoid delaying MaybeTakeEarlyScreenshot() in the + // standard invocation states (b/316110955). if (options_->use_predumped_ui_data) { - return; + return {}; + } + + // Create temporary file for the command's output + std::string outPath = ds.bugreport_internal_dir_ + "/tmp_serialize_perfetto_trace"; + auto outFd = android::base::unique_fd(TEMP_FAILURE_RETRY( + open(outPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); + if (outFd < 0) { + MYLOGE("Could not open %s to serialize perfetto trace.\n", outPath.c_str()); + return {}; } // If a stale file exists already, remove it. unlink(SYSTEM_TRACE_SNAPSHOT); - // If a background system trace is happening and is marked as "suitable for - // bugreport" (i.e. bugreport_score > 0 in the trace config), this command - // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely) - // case that no trace is ongoing, this command is a no-op. - // Note: this should not be enqueued as we need to freeze the trace before - // dumpstate starts. Otherwise the trace ring buffers will contain mostly - // the dumpstate's own activity which is irrelevant. - RunCommand("SERIALIZE PERFETTO TRACE", {"perfetto", "--save-for-bugreport"}, - CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build()); - // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip - // file in the later stages. + MYLOGI("Launching async '%s'", SERIALIZE_PERFETTO_TRACE_TASK.c_str()) + return std::async( + std::launch::async, [this, outPath = std::move(outPath), outFd = std::move(outFd)] { + // If a background system trace is happening and is marked as "suitable for + // bugreport" (i.e. bugreport_score > 0 in the trace config), this command + // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely) + // case that no trace is ongoing, this command is a no-op. + // Note: this should not be enqueued as we need to freeze the trace before + // dumpstate starts. Otherwise the trace ring buffers will contain mostly + // the dumpstate's own activity which is irrelevant. + RunCommand( + SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"}, + CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build(), + false, outFd); + // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip + // file in the later stages. + + return outPath; + }); +} + +void Dumpstate::MaybeWaitForSnapshotSystemTrace(std::future<std::string> task) { + if (!task.valid()) { + return; + } + + WaitForTask(std::move(task), SERIALIZE_PERFETTO_TRACE_TASK, STDOUT_FILENO); } void Dumpstate::MaybeSnapshotUiTraces() { @@ -3420,16 +3460,24 @@ void Dumpstate::MaybeSnapshotUiTraces() { return; } - const std::vector<std::vector<std::string>> dumpTracesForBugReportCommands = { - {"dumpsys", "activity", "service", "SystemUIService", "WMShell", "protolog", - "save-for-bugreport"}, - {"dumpsys", "activity", "service", "SystemUIService", "WMShell", "transitions", "tracing", - "save-for-bugreport"}, + std::vector<std::vector<std::string>> dumpTracesForBugReportCommands = { {"cmd", "input_method", "tracing", "save-for-bugreport"}, {"cmd", "window", "tracing", "save-for-bugreport"}, {"cmd", "window", "shell", "tracing", "save-for-bugreport"}, }; + if (!android_tracing_perfetto_transition_tracing()) { + dumpTracesForBugReportCommands.push_back({"dumpsys", "activity", "service", + "SystemUIService", "WMShell", "transitions", + "tracing", "save-for-bugreport"}); + } + + if (!android_tracing_perfetto_protolog_tracing()) { + dumpTracesForBugReportCommands.push_back({"dumpsys", "activity", "service", + "SystemUIService", "WMShell", "protolog", + "save-for-bugreport"}); + } + for (const auto& command : dumpTracesForBugReportCommands) { RunCommand( // Empty name because it's not intended to be classified as a bugreport section. diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index de732c0dd9..46d949e303 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -570,7 +570,8 @@ class Dumpstate { RunStatus dumpstate(); void MaybeTakeEarlyScreenshot(); - void MaybeSnapshotSystemTrace(); + std::future<std::string> MaybeSnapshotSystemTraceAsync(); + void MaybeWaitForSnapshotSystemTrace(std::future<std::string> task); void MaybeSnapshotUiTraces(); void MaybeAddUiTracesToZip(); diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index fc828864d5..2afabed813 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -16,23 +16,7 @@ #define LOG_TAG "dumpstate_test" -#include "DumpstateInternal.h" -#include "DumpstateService.h" -#include "android/os/BnDumpstate.h" #include "dumpstate.h" -#include "DumpPool.h" - -#include <gmock/gmock.h> -#include <gmock/gmock-matchers.h> -#include <gtest/gtest.h> - -#include <fcntl.h> -#include <libgen.h> -#include <signal.h> -#include <sys/types.h> -#include <unistd.h> -#include <filesystem> -#include <thread> #include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/file.h> @@ -41,10 +25,27 @@ #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <android/hardware/dumpstate/1.1/types.h> +#include <android_tracing.h> #include <cutils/log.h> #include <cutils/properties.h> +#include <fcntl.h> +#include <gmock/gmock-matchers.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <libgen.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> #include <ziparchive/zip_archive.h> +#include <filesystem> +#include <thread> + +#include "DumpPool.h" +#include "DumpstateInternal.h" +#include "DumpstateService.h" +#include "android/os/BnDumpstate.h" + namespace android { namespace os { namespace dumpstate { @@ -999,10 +1000,13 @@ TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) { TEST_F(DumpstateTest, PreDumpUiData) { // These traces are always enabled, i.e. they are always pre-dumped - const std::vector<std::filesystem::path> uiTraces = { - std::filesystem::path{"/data/misc/wmtrace/wm_transition_trace.winscope"}, - std::filesystem::path{"/data/misc/wmtrace/shell_transition_trace.winscope"}, - }; + std::vector<std::filesystem::path> uiTraces; + if (!android_tracing_perfetto_transition_tracing()) { + uiTraces.push_back( + std::filesystem::path{"/data/misc/wmtrace/wm_transition_trace.winscope"}); + uiTraces.push_back( + std::filesystem::path{"/data/misc/wmtrace/shell_transition_trace.winscope"}); + } for (const auto traceFile : uiTraces) { std::system(("rm -f " + traceFile.string()).c_str()); diff --git a/cmds/evemu-record/README.md b/cmds/evemu-record/README.md new file mode 100644 index 0000000000..5d16d51da0 --- /dev/null +++ b/cmds/evemu-record/README.md @@ -0,0 +1,48 @@ +# `evemu-record` + +This is a Rust implementation of the `evemu-record` command from the [FreeDesktop project's evemu +suite][FreeDesktop]. It records the descriptor and events produced by a single input device in a +[simple text-based format][format] that can be replayed using the [`uinput` command on +Android][uinput] or the FreeDesktop evemu tools on other Linux-based platforms. It is included by +default with `userdebug` and `eng` builds of Android. + +The command-line interface is the same as that of the FreeDesktop version, except for +Android-specific features. For usage instructions, run `evemu-record --help`. + +## Usage example + +From a computer connected to the device over ADB, you can start a recording: + +``` +$ adb shell evemu-record > my-recording.evemu +Available devices: +/dev/input/event0: gpio_keys +/dev/input/event1: s2mpg12-power-keys +/dev/input/event2: NVTCapacitiveTouchScreen +/dev/input/event3: NVTCapacitivePen +/dev/input/event4: uinput-folio +/dev/input/event5: ACME Touchpad +Select the device event number [0-5]: 5 +``` + +...then use the input device for a while, and press Ctrl+C to finish. You will now have a +`my-recording.evemu` file that you can examine in a text editor. To replay it, use the [`uinput` +command][uinput]: + +``` +$ adb shell uinput - < my-recording.evemu +``` + +## Android-specific features + +### Timestamp bases + +By default, event timestamps are recorded relative to the time of the first event received during +the recording. Passing `--timestamp-base=boot` causes the timestamps to be recorded relative to the +system boot time instead. While this does not affect the playback of the recording, it can be useful +for matching recorded events with other logs that use such timestamps, such as `dmesg` or the +touchpad gesture debug logs emitted by `TouchpadInputMapper`. + +[FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu +[format]: https://gitlab.freedesktop.org/libevdev/evemu#device-description-format +[uinput]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/cmds/uinput/README.md diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 89c6c0f046..707b9982ab 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -281,6 +281,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.telephony.carrierlock.prebuilt.xml", + src: "android.hardware.telephony.carrierlock.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.telephony.gsm.prebuilt.xml", src: "android.hardware.telephony.gsm.xml", defaults: ["frameworks_native_data_etc_defaults"], @@ -293,6 +299,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.telephony.ims.singlereg.prebuilt.xml", + src: "android.hardware.telephony.ims.singlereg.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.thread_network.prebuilt.xml", src: "android.hardware.thread_network.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/data/etc/android.hardware.location.gps.xml b/data/etc/android.hardware.location.gps.xml index 72ab73228e..2a55370832 100644 --- a/data/etc/android.hardware.location.gps.xml +++ b/data/etc/android.hardware.location.gps.xml @@ -17,6 +17,5 @@ <!-- These are the location-related features for devices that include GPS. --> <permissions> <feature name="android.hardware.location" /> - <feature name="android.hardware.location.network" /> <feature name="android.hardware.location.gps" /> </permissions> diff --git a/data/etc/android.software.contextualsearch.xml b/data/etc/android.software.contextualsearch.xml new file mode 100644 index 0000000000..4564ac8804 --- /dev/null +++ b/data/etc/android.software.contextualsearch.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices supporting contextual search helper. --> +<permissions> + <feature name="android.software.contextualsearch" /> +</permissions> diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 95b8110b6e..beb69f81dd 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -28,7 +28,6 @@ <feature name="android.hardware.audio.output" /> <feature name="android.hardware.location" /> - <feature name="android.hardware.location.network" /> <feature name="android.hardware.bluetooth" /> <feature name="android.hardware.touchscreen" /> <feature name="android.hardware.microphone" /> diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml index 855b11005f..4c9932d4fb 100644 --- a/data/etc/wearable_core_hardware.xml +++ b/data/etc/wearable_core_hardware.xml @@ -36,6 +36,7 @@ <feature name="android.hardware.security.model.compatible" /> <!-- basic system services --> + <feature name="android.software.credentials" /> <feature name="android.software.home_screen" /> <feature name="android.software.secure_lock_screen" /> diff --git a/include/android/asset_manager.h b/include/android/asset_manager.h index 2ac7d4d350..6420cd0064 100644 --- a/include/android/asset_manager.h +++ b/include/android/asset_manager.h @@ -29,6 +29,10 @@ #include <sys/cdefs.h> #include <sys/types.h> +#if defined(__APPLE__) +typedef off_t off64_t; // Mac OSX does not define off64_t +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 6a91d9836e..bec3283cd8 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -65,9 +65,19 @@ #ifndef ANDROID_CHOREOGRAPHER_H #define ANDROID_CHOREOGRAPHER_H +#include <stddef.h> #include <stdint.h> #include <sys/cdefs.h> +// This file may also be built on glibc or on Windows/MacOS libc's, so no-op +// and deprecated definitions are provided. +#if !defined(__INTRODUCED_IN) +#define __INTRODUCED_IN(__api_level) /* nothing */ +#endif +#if !defined(__DEPRECATED_IN) +#define __DEPRECATED_IN(__api_level, ...) __attribute__((__deprecated__)) +#endif + __BEGIN_DECLS struct AChoreographer; diff --git a/include/android/input.h b/include/android/input.h index 16d86af44c..fec56f02f9 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -1490,6 +1490,17 @@ int32_t AMotionEvent_getClassification(const AInputEvent* motion_event) */ const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) __INTRODUCED_IN(31); +/** + * Creates a java android.view.InputEvent object that is a copy of the specified native + * {@link AInputEvent}. + * + * Specified {@link AInputEvent} is require to be a valid {@link MotionEvent} or {@link KeyEvent} + * object. + * + * Available since API level 35. + */ +jobject AInputEvent_toJava(JNIEnv* env, const AInputEvent* aInputEvent) __INTRODUCED_IN(35); + struct AInputQueue; /** * Input queue diff --git a/include/android/keycodes.h b/include/android/keycodes.h index f8fb256fae..79cdbcaf7b 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -839,6 +839,10 @@ enum { AKEYCODE_MACRO_3 = 315, /** User customizable key #4. */ AKEYCODE_MACRO_4 = 316, + /** Open Emoji picker */ + AKEYCODE_EMOJI_PICKER = 317, + /** Take Screenshot */ + AKEYCODE_SCREENSHOT = 318, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 9d2c79139f..3c82d888b5 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -123,7 +123,8 @@ typedef struct APerformanceHintSession APerformanceHintSession; * * @return APerformanceHintManager instance on success, nullptr on failure. */ -APerformanceHintManager* _Nullable APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); +APerformanceHintManager* _Nullable APerformanceHint_getManager() + __INTRODUCED_IN(__ANDROID_API_T__); /** * Creates a session for the given set of threads and sets their initial target work @@ -232,14 +233,14 @@ int APerformanceHint_setPreferPowerEfficiency( * @param workDuration The {@link AWorkDuration} structure of times the thread group took to * complete its last task in nanoseconds breaking down into different components. * - * The work period start timestamp, actual total duration and actual CPU duration must be - * positive. + * The work period start timestamp and actual total duration must be greater than zero. * - * The actual GPU duration must be non-negative. If the actual GPU duration is 0, it means - * the actual GPU duration is not measured. + * The actual CPU and GPU durations must be greater than or equal to zero, and at least one + * of them must be greater than zero. When one of them is equal to zero, it means that type + * of work was not measured for this workload. * * @return 0 on success. - * EINVAL if session is nullptr or any duration is an invalid number. + * EINVAL if any duration is an invalid number. * EPIPE if communication with the system service has failed. */ int APerformanceHint_reportActualWorkDuration2( @@ -260,14 +261,15 @@ AWorkDuration* _Nonnull AWorkDuration_create() __INTRODUCED_IN(__ANDROID_API_V__ * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} */ -void AWorkDuration_release(AWorkDuration* _Nonnull WorkDuration) __INTRODUCED_IN(__ANDROID_API_V__); +void AWorkDuration_release(AWorkDuration* _Nonnull aWorkDuration) + __INTRODUCED_IN(__ANDROID_API_V__); /** * Sets the work period start timestamp in nanoseconds. * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} * @param workPeriodStartTimestampNanos The work period start timestamp in nanoseconds based on - * CLOCK_MONOTONIC about when the work starts, the timestamp must be positive. + * CLOCK_MONOTONIC about when the work starts. This timestamp must be greater than zero. */ void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWorkDuration, int64_t workPeriodStartTimestampNanos) __INTRODUCED_IN(__ANDROID_API_V__); @@ -276,8 +278,8 @@ void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWor * Sets the actual total work duration in nanoseconds. * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} - * @param actualTotalDurationNanos The actual total work duration in nanoseconds, the number must be - * positive. + * @param actualTotalDurationNanos The actual total work duration in nanoseconds. This number must + * be greater than zero. */ void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDuration, int64_t actualTotalDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); @@ -286,8 +288,9 @@ void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDura * Sets the actual CPU work duration in nanoseconds. * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} - * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds, the number must be - * positive. + * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds. This number must be + * greater than or equal to zero. If it is equal to zero, that means the CPU was not + * measured. */ void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, int64_t actualCpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); @@ -297,7 +300,7 @@ void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDurati * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}. * @param actualGpuDurationNanos The actual GPU work duration in nanoseconds, the number must be - * non-negative. If the actual GPU duration is 0, it means the actual GPU duration is + * greater than or equal to zero. If it is equal to zero, that means the GPU was not * measured. */ void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 14167e6467..0765e01004 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -537,9 +537,8 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* _Nonnull t /** * Sets the desired extended range brightness for the layer. This only applies for layers whose - * dataspace has RANGE_EXTENDED set on it. - * - * Available since API level 34. + * dataspace has RANGE_EXTENDED set on it. See: ASurfaceTransaction_setDesiredHdrHeadroom, prefer + * 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 @@ -573,6 +572,12 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* _Nonnull t * determined entirely by the dataspace being used (ie, typically SDR * however PQ or HLG transfer functions will still result in HDR) * + * When called after ASurfaceTransaction_setDesiredHdrHeadroom, the + * desiredRatio will override the desiredHeadroom provided by + * ASurfaceTransaction_setDesiredHdrHeadroom. Conversely, when called before + * ASurfaceTransaction_setDesiredHdrHeadroom, the desiredHeadroom provided by + *. ASurfaceTransaction_setDesiredHdrHeadroom will override the desiredRatio. + * * Must be finite && >= 1.0f * * Available since API level 34. @@ -583,6 +588,45 @@ void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* _Nonnul __INTRODUCED_IN(__ANDROID_API_U__); /** + * 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 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. + * + * While requesting a large desired ratio will result in the most + * dynamic range, voluntarily reducing the requested range can help + * improve battery life as well as can improve quality by ensuring + * greater bit depth is allocated to the luminance range in use. + * + * Default value is 0.0f and indicates that the system will choose the best + * headroom for this surface control's content. Typically, this means that + * HLG/PQ encoded content will be displayed with some HDR headroom greater + * than 1.0. + * + * When called after ASurfaceTransaction_setExtendedRangeBrightness, the + * desiredHeadroom will override the desiredRatio provided by + * ASurfaceTransaction_setExtendedRangeBrightness. Conversely, when called + * before ASurfaceTransaction_setExtendedRangeBrightness, the desiredRatio + * provided by ASurfaceTransaction_setExtendedRangeBrightness will override + * the desiredHeadroom. + * + * Must be finite && >= 1.0f or 0.0f to indicate there is no desired + * headroom. + * + * Available since API level 35. + */ +void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + float desiredHeadroom) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control, * frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS). * diff --git a/include/android/thermal.h b/include/android/thermal.h index 0b57e9376d..fa168cdecb 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -111,7 +111,7 @@ typedef struct AThermalManager AThermalManager; * It's passed the updated thermal status as parameter, as well as the * pointer provided by the client that registered a callback. */ -typedef void (*AThermal_StatusCallback)(void* data, AThermalStatus status); +typedef void (*AThermal_StatusCallback)(void* _Nullable data, AThermalStatus status); /** * Acquire an instance of the thermal manager. This must be freed using @@ -121,7 +121,7 @@ typedef void (*AThermal_StatusCallback)(void* data, AThermalStatus status); * * @return manager instance on success, nullptr on failure. */ -AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30); +AThermalManager* _Nonnull AThermal_acquireManager() __INTRODUCED_IN(30); /** * Release the thermal manager pointer acquired via @@ -131,7 +131,7 @@ AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30); * * @param manager The manager to be released. */ -void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30); +void AThermal_releaseManager(AThermalManager* _Nonnull manager) __INTRODUCED_IN(30); /** * Gets the current thermal status. @@ -143,7 +143,8 @@ void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30); * * @return current thermal status, ATHERMAL_STATUS_ERROR on failure. */ -AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30); +AThermalStatus +AThermal_getCurrentThermalStatus(AThermalManager* _Nonnull manager) __INTRODUCED_IN(30); /** * Register the thermal status listener for thermal status change. @@ -160,8 +161,9 @@ AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTR * EPERM if the required permission is not held. * EPIPE if communication with the system service has failed. */ -int AThermal_registerThermalStatusListener(AThermalManager *manager, - AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30); +int AThermal_registerThermalStatusListener(AThermalManager* _Nonnull manager, + AThermal_StatusCallback _Nullable callback, + void* _Nullable data) __INTRODUCED_IN(30); /** * Unregister the thermal status listener previously resgistered. @@ -178,8 +180,9 @@ int AThermal_registerThermalStatusListener(AThermalManager *manager, * EPERM if the required permission is not held. * EPIPE if communication with the system service has failed. */ -int AThermal_unregisterThermalStatusListener(AThermalManager *manager, - AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30); +int AThermal_unregisterThermalStatusListener(AThermalManager* _Nonnull manager, + AThermal_StatusCallback _Nullable callback, + void* _Nullable data) __INTRODUCED_IN(30); /** * Provides an estimate of how much thermal headroom the device currently has before @@ -219,8 +222,8 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, * as described above. Returns NaN if the device does not support this functionality or * if this function is called significantly faster than once per second. */ -float AThermal_getThermalHeadroom(AThermalManager *manager, - int forecastSeconds) __INTRODUCED_IN(31); +float AThermal_getThermalHeadroom(AThermalManager* _Nonnull manager, + int forecastSeconds) __INTRODUCED_IN(31); /** * This struct defines an instance of headroom threshold value and its status. @@ -282,9 +285,10 @@ struct AThermalHeadroomThreshold { * EPIPE if communication with the system service has failed. * ENOSYS if the feature is disabled by the current system. */ -int AThermal_getThermalHeadroomThresholds(AThermalManager* manager, - const AThermalHeadroomThreshold ** outThresholds, - size_t* size) __INTRODUCED_IN(35); +int AThermal_getThermalHeadroomThresholds(AThermalManager* _Nonnull manager, + const AThermalHeadroomThreshold* _Nonnull + * _Nullable outThresholds, + size_t* _Nonnull size) __INTRODUCED_IN(35); #ifdef __cplusplus } diff --git a/include/ftl/details/future.h b/include/ftl/details/future.h index df1323e8be..8d82e0ffd2 100644 --- a/include/ftl/details/future.h +++ b/include/ftl/details/future.h @@ -73,8 +73,18 @@ class BaseFuture<Self, T, std::future> { return std::get<Impl>(self()).get(); } + template <class Rep, class Period> + std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const { + if (std::holds_alternative<T>(self())) { + return std::future_status::ready; + } + + return std::get<Impl>(self()).wait_for(timeout_duration); + } + private: auto& self() { return static_cast<Self&>(*this).future_; } + const auto& self() const { return static_cast<const Self&>(*this).future_; } }; template <typename Self, typename T> @@ -90,6 +100,15 @@ class BaseFuture<Self, T, std::shared_future> { return std::get<Impl>(self()).get(); } + template <class Rep, class Period> + std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const { + if (std::holds_alternative<T>(self())) { + return std::future_status::ready; + } + + return std::get<Impl>(self()).wait_for(timeout_duration); + } + private: const auto& self() const { return static_cast<const Self&>(*this).future_; } }; diff --git a/include/ftl/expected.h b/include/ftl/expected.h new file mode 100644 index 0000000000..12b6102b6f --- /dev/null +++ b/include/ftl/expected.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. + */ + +#pragma once + +#include <android-base/expected.h> +#include <ftl/optional.h> + +#include <utility> + +namespace android::ftl { + +// Superset of base::expected<T, E> with monadic operations. +// +// TODO: Extend std::expected<T, E> in C++23. +// +template <typename T, typename E> +struct Expected final : base::expected<T, E> { + using Base = base::expected<T, E>; + using Base::expected; + + using Base::error; + using Base::has_value; + using Base::value; + + template <typename P> + constexpr bool has_error(P predicate) const { + return !has_value() && predicate(error()); + } + + constexpr Optional<T> value_opt() const& { + return has_value() ? Optional(value()) : std::nullopt; + } + + constexpr Optional<T> value_opt() && { + return has_value() ? Optional(std::move(value())) : std::nullopt; + } + + // Delete new for this class. Its base doesn't have a virtual destructor, and + // if it got deleted via base class pointer, it would cause undefined + // behavior. There's not a good reason to allocate this object on the heap + // anyway. + static void* operator new(size_t) = delete; + static void* operator new[](size_t) = delete; +}; + +template <typename E> +constexpr auto Unexpected(E&& error) { + return base::unexpected(std::forward<E>(error)); +} + +} // namespace android::ftl diff --git a/include/ftl/fake_guard.h b/include/ftl/fake_guard.h index bacd1b29ef..e6012516fc 100644 --- a/include/ftl/fake_guard.h +++ b/include/ftl/fake_guard.h @@ -85,6 +85,5 @@ struct [[clang::scoped_lockable]] FakeGuard final { #define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard -// The void argument suppresses a warning about zero variadic macro arguments. #define FTL_FAKE_GUARD(...) \ - FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, void)(__VA_ARGS__) + FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, )(__VA_ARGS__) diff --git a/include/ftl/future.h b/include/ftl/future.h index c78f9b76b6..dad180ff8e 100644 --- a/include/ftl/future.h +++ b/include/ftl/future.h @@ -51,6 +51,7 @@ class Future final : public details::BaseFuture<Future<T, FutureImpl>, T, Future // Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the // following are defined for either FutureImpl: using Base::get; + using Base::wait_for; // Attaches a continuation to the future. The continuation is a function that maps T to either R // or ftl::Future<R>. In the former case, the chain wraps the result in a future as if by diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h index 49cde7fedc..83d5967464 100644 --- a/include/ftl/small_map.h +++ b/include/ftl/small_map.h @@ -107,12 +107,20 @@ class SmallMap final { template <typename Q, typename W, std::size_t M, typename E> SmallMap(SmallMap<Q, W, M, E> other) : map_(std::move(other.map_)) {} + static constexpr size_type static_capacity() { return N; } + size_type max_size() const { return map_.max_size(); } size_type size() const { return map_.size(); } bool empty() const { return map_.empty(); } // Returns whether the map is backed by static or dynamic storage. - bool dynamic() const { return map_.dynamic(); } + bool dynamic() const { + if constexpr (static_capacity() > 0) { + return map_.dynamic(); + } else { + return true; + } + } iterator begin() { return map_.begin(); } const_iterator begin() const { return cbegin(); } @@ -171,9 +179,15 @@ class SmallMap final { return {it, false}; } - auto& ref = map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(std::forward<Args>(args)...)); - return {&ref, true}; + decltype(auto) ref_or_it = + map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + + if constexpr (static_capacity() > 0) { + return {&ref_or_it, true}; + } else { + return {ref_or_it, true}; + } } // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h index 11294c3ac8..43e9fac5e2 100644 --- a/include/ftl/small_vector.h +++ b/include/ftl/small_vector.h @@ -124,30 +124,29 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma DISPATCH(size_type, size, const) DISPATCH(bool, empty, const) - // noexcept to suppress warning about zero variadic macro arguments. - DISPATCH(iterator, begin, noexcept) + DISPATCH(iterator, begin, ) DISPATCH(const_iterator, begin, const) DISPATCH(const_iterator, cbegin, const) - DISPATCH(iterator, end, noexcept) + DISPATCH(iterator, end, ) DISPATCH(const_iterator, end, const) DISPATCH(const_iterator, cend, const) - DISPATCH(reverse_iterator, rbegin, noexcept) + DISPATCH(reverse_iterator, rbegin, ) DISPATCH(const_reverse_iterator, rbegin, const) DISPATCH(const_reverse_iterator, crbegin, const) - DISPATCH(reverse_iterator, rend, noexcept) + DISPATCH(reverse_iterator, rend, ) DISPATCH(const_reverse_iterator, rend, const) DISPATCH(const_reverse_iterator, crend, const) - DISPATCH(iterator, last, noexcept) + DISPATCH(iterator, last, ) DISPATCH(const_iterator, last, const) - DISPATCH(reference, front, noexcept) + DISPATCH(reference, front, ) DISPATCH(const_reference, front, const) - DISPATCH(reference, back, noexcept) + DISPATCH(reference, back, ) DISPATCH(const_reference, back, const) reference operator[](size_type i) { @@ -211,13 +210,13 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma // // The last() and end() iterators are invalidated. // - DISPATCH(void, pop_back, noexcept) + DISPATCH(void, pop_back, ) // Removes all elements. // // All iterators are invalidated. // - DISPATCH(void, clear, noexcept) + DISPATCH(void, clear, ) #undef DISPATCH diff --git a/include/input/AccelerationCurve.h b/include/input/AccelerationCurve.h new file mode 100644 index 0000000000..0cf648a2f7 --- /dev/null +++ b/include/input/AccelerationCurve.h @@ -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. + */ + +#pragma once + +#include <cstdint> +#include <vector> + +namespace android { + +/** + * Describes a section of an acceleration curve as a function which outputs a scaling factor (gain) + * for the pointer movement, given the speed of the mouse or finger (in mm/s): + * + * gain(input_speed_mm_per_s) = baseGain + reciprocal / input_speed_mm_per_s + */ +struct AccelerationCurveSegment { + /** + * The maximum pointer speed at which this segment should apply, in mm/s. The last segment in a + * curve should always set this to infinity. + */ + double maxPointerSpeedMmPerS; + /** The gain for this segment before the reciprocal is taken into account. */ + double baseGain; + /** The reciprocal part of the formula, which should be divided by the input speed. */ + double reciprocal; +}; + +/** + * Creates an acceleration curve for the given pointer sensitivity value. The sensitivity value + * should be between -7 (for the lowest sensitivity) and 7, inclusive. + */ +std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivity( + int32_t sensitivity); + +} // namespace android diff --git a/include/input/Input.h b/include/input/Input.h index 1c4ea6b416..a84dcfc63c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -180,7 +180,8 @@ static constexpr size_t MAX_POINTERS = 16; * Declare a concrete type for the NDK's input event forward declaration. */ struct AInputEvent { - virtual ~AInputEvent() { } + virtual ~AInputEvent() {} + bool operator==(const AInputEvent&) const = default; }; /* @@ -515,6 +516,8 @@ struct PointerProperties { PointerProperties& operator=(const PointerProperties&) = default; }; +std::ostream& operator<<(std::ostream& out, const PointerProperties& properties); + // TODO(b/211379801) : Use a strong type from ftl/mixins.h instead using DeviceId = int32_t; @@ -543,6 +546,8 @@ public: static int32_t nextId(); + bool operator==(const InputEvent&) const = default; + protected: void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac); @@ -596,6 +601,8 @@ public: static const char* actionToString(int32_t action); + bool operator==(const KeyEvent&) const = default; + protected: int32_t mAction; int32_t mFlags; @@ -915,6 +922,9 @@ public: // The rounding precision for transformed motion events. static constexpr float ROUNDING_PRECISION = 0.001f; + bool operator==(const MotionEvent&) const; + inline bool operator!=(const MotionEvent& o) const { return !(*this == o); }; + protected: int32_t mAction; int32_t mActionButton; diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index b7751f704a..57b659d9ee 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -75,6 +75,17 @@ struct InputDeviceIdentifier { bool operator!=(const InputDeviceIdentifier&) const = default; }; +/** + * Holds View related behaviors for an InputDevice. + */ +struct InputDeviceViewBehavior { + /** + * The smooth scroll behavior that applies for all source/axis, if defined by the device. + * Empty optional if the device has not specified the default smooth scroll behavior. + */ + std::optional<bool> shouldSmoothScroll; +}; + /* Types of input device sensors. Keep sync with core/java/android/hardware/Sensor.java */ enum class InputDeviceSensorType : int32_t { ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, @@ -266,7 +277,8 @@ public: void initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, int32_t associatedDisplayId); + bool isExternal, bool hasMic, int32_t associatedDisplayId, + InputDeviceViewBehavior viewBehavior = {{}}); inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } @@ -298,6 +310,8 @@ public: return mKeyboardLayoutInfo; } + inline const InputDeviceViewBehavior& getViewBehavior() const { return mViewBehavior; } + inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) { mKeyCharacterMap = value; } @@ -359,6 +373,8 @@ private: std::unordered_map<int32_t, InputDeviceLightInfo> mLights; /* Map from battery ID to battery info */ std::unordered_map<int32_t, InputDeviceBatteryInfo> mBatteries; + /** The View related behaviors for the device. */ + InputDeviceViewBehavior mViewBehavior; }; /* Types of input device configuration files. */ diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 59b9495c42..42dcd3c394 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -35,18 +35,16 @@ #include <android-base/result.h> #include <android-base/unique_fd.h> +#include <android/os/InputChannelCore.h> #include <binder/IBinder.h> -#include <binder/Parcelable.h> #include <input/Input.h> #include <input/InputVerifier.h> #include <sys/stat.h> #include <ui/Transform.h> #include <utils/BitSet.h> #include <utils/Errors.h> -#include <utils/RefBase.h> #include <utils/Timers.h> - namespace android { class Parcel; @@ -231,18 +229,15 @@ struct InputMessage { * input messages across processes. Each channel has a descriptive name for debugging purposes. * * Each endpoint has its own InputChannel object that specifies its file descriptor. + * For parceling, this relies on android::os::InputChannelCore, defined in aidl. * * The input channel is closed when all references to it are released. */ -class InputChannel : public Parcelable { +class InputChannel : private android::os::InputChannelCore { public: - static std::unique_ptr<InputChannel> create(const std::string& name, - android::base::unique_fd fd, sp<IBinder> token); - InputChannel() = default; - InputChannel(const InputChannel& other) - : mName(other.mName), mFd(other.dupFd()), mToken(other.mToken){}; - InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); - ~InputChannel() override; + static std::unique_ptr<InputChannel> create(android::os::InputChannelCore&& parceledChannel); + ~InputChannel(); + /** * Create a pair of input channels. * The two returned input channels are equivalent, and are labeled as "server" and "client" @@ -254,9 +249,8 @@ public: std::unique_ptr<InputChannel>& outServerChannel, std::unique_ptr<InputChannel>& outClientChannel); - inline std::string getName() const { return mName; } - inline const android::base::unique_fd& getFd() const { return mFd; } - inline sp<IBinder> getToken() const { return mToken; } + inline std::string getName() const { return name; } + inline int getFd() const { return fd.get(); } /* Send a message to the other endpoint. * @@ -283,13 +277,37 @@ public: */ status_t receiveMessage(InputMessage* msg); + /* Tells whether there is a message in the channel available to be received. + * + * This is only a performance hint and may return false negative results. Clients should not + * rely on availability of the message based on the return value. + */ + bool probablyHasInput() const; + + /* Wait until there is a message in the channel. + * + * The |timeout| specifies how long to block waiting for an input event to appear. Negative + * values are not allowed. + * + * In some cases returning before timeout expiration can happen without a message available. + * This could happen after the channel was closed on the other side. Another possible reason + * is incorrect setup of the channel. + */ + void waitForMessage(std::chrono::milliseconds timeout) const; + /* Return a new object that has a duplicate of this channel's fd. */ std::unique_ptr<InputChannel> dup() const; - void copyTo(InputChannel& outChannel) const; + void copyTo(android::os::InputChannelCore& outChannel) const; - status_t readFromParcel(const android::Parcel* parcel) override; - status_t writeToParcel(android::Parcel* parcel) const override; + /** + * Similar to "copyTo", but it takes ownership of the provided InputChannel (and after this is + * called, it destroys it). + * @param from the InputChannel that should be converted to InputChannelCore + * @param outChannel the pre-allocated InputChannelCore to which to transfer the 'from' channel + */ + static void moveChannel(std::unique_ptr<InputChannel> from, + android::os::InputChannelCore& outChannel); /** * The connection token is used to identify the input connection, i.e. @@ -305,26 +323,11 @@ public: */ sp<IBinder> getConnectionToken() const; - bool operator==(const InputChannel& inputChannel) const { - struct stat lhs, rhs; - if (fstat(mFd.get(), &lhs) != 0) { - return false; - } - if (fstat(inputChannel.getFd().get(), &rhs) != 0) { - return false; - } - // If file descriptors are pointing to same inode they are duplicated fds. - return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken && - lhs.st_ino == rhs.st_ino; - } - private: - base::unique_fd dupFd() const; - - std::string mName; - base::unique_fd mFd; + static std::unique_ptr<InputChannel> create(const std::string& name, + android::base::unique_fd fd, sp<IBinder> token); - sp<IBinder> mToken; + InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); }; /* @@ -339,7 +342,7 @@ public: ~InputPublisher(); /* Gets the underlying input channel. */ - inline std::shared_ptr<InputChannel> getChannel() { return mChannel; } + inline InputChannel& getChannel() const { return *mChannel; } /* Publishes a key event to the input channel. * @@ -518,6 +521,13 @@ public: */ int32_t getPendingBatchSource() const; + /* Returns true when there is *likely* a pending batch or a pending event in the channel. + * + * This is only a performance hint and may return false negative results. Clients should not + * rely on availability of the message based on the return value. + */ + bool probablyHasInput() const; + std::string dump() const; private: diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h index 83fffa37c6..3470be4dce 100644 --- a/include/input/PrintTools.h +++ b/include/input/PrintTools.h @@ -117,11 +117,12 @@ std::string dumpMapKeys(const std::map<K, V>& map, template <typename T> std::string dumpVector(const std::vector<T>& values, std::string (*valueToString)(const T&) = constToString) { - std::string dump = valueToString(values[0]); - for (size_t i = 1; i < values.size(); i++) { - dump += ", " + valueToString(values[i]); + std::string out; + for (const auto& value : values) { + out += out.empty() ? "[" : ", "; + out += valueToString(value); } - return dump; + return out.empty() ? "[]" : (out + "]"); } const char* toString(bool value); diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h index b78f63e1ae..7c58c87f8b 100644 --- a/include/input/VelocityControl.h +++ b/include/input/VelocityControl.h @@ -16,7 +16,10 @@ #pragma once +#include <vector> + #include <android-base/stringprintf.h> +#include <input/AccelerationCurve.h> #include <input/Input.h> #include <input/VelocityTracker.h> #include <utils/Timers.h> @@ -86,12 +89,7 @@ struct VelocityControlParameters { class VelocityControl { public: VelocityControl(); - - /* Gets the various parameters. */ - const VelocityControlParameters& getParameters() const; - - /* Sets the various parameters. */ - void setParameters(const VelocityControlParameters& parameters); + virtual ~VelocityControl() {} /* Resets the current movement counters to zero. * This has the effect of nullifying any acceleration. */ @@ -101,16 +99,55 @@ public: * scaled / accelerated delta based on the current velocity. */ void move(nsecs_t eventTime, float* deltaX, float* deltaY); -private: +protected: + virtual void scaleDeltas(float* deltaX, float* deltaY) = 0; + // If no movements are received within this amount of time, // we assume the movement has stopped and reset the movement counters. static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms - VelocityControlParameters mParameters; - nsecs_t mLastMovementTime; float mRawPositionX, mRawPositionY; VelocityTracker mVelocityTracker; }; +/** + * Velocity control using a simple acceleration curve where the acceleration factor increases + * linearly with movement speed, subject to minimum and maximum values. + */ +class SimpleVelocityControl : public VelocityControl { +public: + /** Gets the various parameters. */ + const VelocityControlParameters& getParameters() const; + + /** Sets the various parameters. */ + void setParameters(const VelocityControlParameters& parameters); + +protected: + virtual void scaleDeltas(float* deltaX, float* deltaY) override; + +private: + VelocityControlParameters mParameters; +}; + +/** Velocity control using a curve made up of multiple reciprocal segments. */ +class CurvedVelocityControl : public VelocityControl { +public: + CurvedVelocityControl(); + + /** Sets the curve to be used for acceleration. */ + void setCurve(const std::vector<AccelerationCurveSegment>& curve); + + void setAccelerationEnabled(bool enabled); + +protected: + virtual void scaleDeltas(float* deltaX, float* deltaY) override; + +private: + const AccelerationCurveSegment& segmentForSpeed(float speedMmPerS); + + bool mAccelerationEnabled = true; + std::vector<AccelerationCurveSegment> mCurveSegments; +}; + } // namespace android diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h index 21a28770b6..222dac8557 100644 --- a/include/input/VirtualInputDevice.h +++ b/include/input/VirtualInputDevice.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright 2023 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. @@ -64,6 +64,8 @@ public: class VirtualMouse : public VirtualInputDevice { public: + // Expose to share with VirtualStylus. + static const std::map<int, UinputAction> BUTTON_ACTION_MAPPING; VirtualMouse(android::base::unique_fd fd); virtual ~VirtualMouse() override; bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction, @@ -74,12 +76,13 @@ public: std::chrono::nanoseconds eventTime); private: - static const std::map<int, UinputAction> BUTTON_ACTION_MAPPING; static const std::map<int, int> BUTTON_CODE_MAPPING; }; class VirtualTouchscreen : public VirtualInputDevice { public: + // Expose to share with VirtualStylus. + static const std::map<int, UinputAction> TOUCH_ACTION_MAPPING; VirtualTouchscreen(android::base::unique_fd fd); virtual ~VirtualTouchscreen() override; // TODO(b/259554911): changing float parameters to int32_t. @@ -88,9 +91,7 @@ public: std::chrono::nanoseconds eventTime); private: - static const std::map<int, UinputAction> TOUCH_ACTION_MAPPING; static const std::map<int, int> TOOL_TYPE_MAPPING; - /* The set of active touch pointers on this device. * We only allow pointer id to go up to MAX_POINTERS because the maximum slots of virtual * touchscreen is set up with MAX_POINTERS. Note that in other cases Android allows pointer id @@ -101,4 +102,24 @@ private: bool handleTouchDown(int32_t pointerId, std::chrono::nanoseconds eventTime); bool handleTouchUp(int32_t pointerId, std::chrono::nanoseconds eventTime); }; + +class VirtualStylus : public VirtualInputDevice { +public: + VirtualStylus(android::base::unique_fd fd); + ~VirtualStylus() override; + bool writeMotionEvent(int32_t toolType, int32_t action, int32_t locationX, int32_t locationY, + int32_t pressure, int32_t tiltX, int32_t tiltY, + std::chrono::nanoseconds eventTime); + bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction, + std::chrono::nanoseconds eventTime); + +private: + static const std::map<int, int> TOOL_TYPE_MAPPING; + static const std::map<int, int> BUTTON_CODE_MAPPING; + // True if the stylus is touching or hovering on the screen. + bool mIsStylusDown; + bool handleStylusDown(uint16_t tool, std::chrono::nanoseconds eventTime); + bool handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime); +}; + } // namespace android diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h index 9e426d3ea3..c50bc4a188 100644 --- a/include/powermanager/PowerHalController.h +++ b/include/powermanager/PowerHalController.h @@ -62,7 +62,15 @@ public: virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; + virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) override; virtual HalResult<int64_t> getHintSessionPreferredRate() override; + virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel( + int tgid, int uid) override; + virtual HalResult<void> closeSessionChannel(int tgid, int uid) override; private: std::mutex mConnectedHalMutex; @@ -75,7 +83,7 @@ private: std::shared_ptr<HalWrapper> initHal(); template <typename T> - HalResult<T> processHalResult(HalResult<T> result, const char* functionName); + HalResult<T> processHalResult(HalResult<T>&& result, const char* functionName); }; // ------------------------------------------------------------------------------------------------- diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h index 4e4a1b000d..e2da014606 100644 --- a/include/powermanager/PowerHalWrapper.h +++ b/include/powermanager/PowerHalWrapper.h @@ -14,19 +14,22 @@ * limitations under the License. */ -#ifndef ANDROID_POWERHALWRAPPER_H -#define ANDROID_POWERHALWRAPPER_H +#pragma once #include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/ChannelConfig.h> #include <aidl/android/hardware/power/IPower.h> #include <aidl/android/hardware/power/IPowerHintSession.h> #include <aidl/android/hardware/power/Mode.h> +#include <aidl/android/hardware/power/SessionConfig.h> #include <android-base/thread_annotations.h> #include <android/hardware/power/1.1/IPower.h> #include <android/hardware/power/1.2/IPower.h> #include <android/hardware/power/1.3/IPower.h> #include <binder/Status.h> +#include <utility> + namespace android { namespace power { @@ -42,44 +45,63 @@ enum class HalSupport { template <typename T> class HalResult { public: - static HalResult<T> ok(T value) { return HalResult(value); } - static HalResult<T> failed(std::string msg) { - return HalResult(std::move(msg), /* unsupported= */ false); - } + static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); } + static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); } + static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); } static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); } - static HalResult<T> fromStatus(const binder::Status& status, T data) { + static HalResult<T> fromStatus(const binder::Status& status, T&& data) { if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { return HalResult<T>::unsupported(); } if (status.isOk()) { - return HalResult<T>::ok(data); + return HalResult<T>::ok(std::forward<T>(data)); } return HalResult<T>::failed(std::string(status.toString8().c_str())); } - static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T data) { + static HalResult<T> fromStatus(const binder::Status& status, T& data) { + return HalResult<T>::fromStatus(status, T{data}); + } + + static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T&& data) { if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { return HalResult<T>::unsupported(); } if (status.isOk()) { - return HalResult<T>::ok(data); + return HalResult<T>::ok(std::forward<T>(data)); } return HalResult<T>::failed(std::string(status.getDescription())); } + static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T& data) { + return HalResult<T>::fromStatus(status, T{data}); + } + template <typename R> - static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) { - return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description()); + static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) { + return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data)) + : HalResult<T>::failed(ret.description()); + } + + template <typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) { + return HalResult<T>::fromReturn(ret, T{data}); } template <typename R> static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status, - T data) { - return ret.isOk() ? HalResult<T>::fromStatus(status, data) + T&& data) { + return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data)) : HalResult<T>::failed(ret.description()); } + template <typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status, + T& data) { + return HalResult<T>::fromReturn(ret, status, T{data}); + } + // This will throw std::bad_optional_access if this result is not ok. const T& value() const { return mValue.value(); } bool isOk() const { return !mUnsupported && mValue.has_value(); } @@ -92,8 +114,8 @@ private: std::string mErrorMessage; bool mUnsupported; - explicit HalResult(T value) - : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {} + explicit HalResult(T&& value) + : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {} explicit HalResult(std::string errorMessage, bool unsupported) : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {} }; @@ -158,7 +180,15 @@ public: virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) = 0; + virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) = 0; virtual HalResult<int64_t> getHintSessionPreferredRate() = 0; + virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, + int uid) = 0; + virtual HalResult<void> closeSessionChannel(int tgid, int uid) = 0; }; // Empty Power HAL wrapper that ignores all api calls. @@ -173,11 +203,22 @@ public: HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; + HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) override; HalResult<int64_t> getHintSessionPreferredRate() override; + HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, + int uid) override; + HalResult<void> closeSessionChannel(int tgid, int uid) override; + +protected: + virtual const char* getUnsupportedMessage(); }; // Wrapper for the HIDL Power HAL v1.0. -class HidlHalWrapperV1_0 : public HalWrapper { +class HidlHalWrapperV1_0 : public EmptyHalWrapper { public: explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0) : mHandleV1_0(std::move(handleV1_0)) {} @@ -186,14 +227,11 @@ public: HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, int32_t durationMs) override; HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; - HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( - int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos) override; - HalResult<int64_t> getHintSessionPreferredRate() override; protected: const sp<hardware::power::V1_0::IPower> mHandleV1_0; virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data); + const char* getUnsupportedMessage(); private: HalResult<void> setInteractive(bool enabled); @@ -238,7 +276,7 @@ protected: }; // Wrapper for the AIDL Power HAL. -class AidlHalWrapper : public HalWrapper { +class AidlHalWrapper : public EmptyHalWrapper { public: explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle) : mHandle(std::move(handle)) {} @@ -250,7 +288,19 @@ public: HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; + HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) override; + HalResult<int64_t> getHintSessionPreferredRate() override; + HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, + int uid) override; + HalResult<void> closeSessionChannel(int tgid, int uid) override; + +protected: + const char* getUnsupportedMessage() override; private: // Control access to the boost and mode supported arrays. @@ -274,5 +324,3 @@ private: }; // namespace power }; // namespace android - -#endif // ANDROID_POWERHALWRAPPER_H diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h index d50c5f846e..d8f9db4882 100644 --- a/include/private/performance_hint_private.h +++ b/include/private/performance_hint_private.h @@ -53,6 +53,26 @@ enum SessionHint: int32_t { * CPU resources to what was used previously, and must wake up if inactive. */ CPU_LOAD_RESUME = 3, + + /** + * This hint indicates an increase in GPU workload intensity. It means that + * this hint session needs extra GPU resources to meet the target duration. + * This hint must be sent before reporting the actual duration to the session. + */ + GPU_LOAD_UP = 5, + + /** + * This hint indicates a decrease in GPU workload intensity. It means that + * this hint session can reduce GPU resources and still meet the target duration. + */ + GPU_LOAD_DOWN = 6, + + /* + * This hint indicates an upcoming GPU workload that is completely changed and + * unknown. It means that the hint session should reset GPU resources to a known + * baseline to prepare for an arbitrary load, and must wake up if inactive. + */ + GPU_LOAD_RESET = 7, }; /** diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 1a9766d72e..319716ec81 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["frameworks_native_libs_arect_license"], + default_team: "trendy_team_android_core_graphics_stack", } // Added automatically by a large-scale-change diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h index ce9cd1cebe..7da8d51ccd 100644 --- a/libs/battery/MultiStateCounter.h +++ b/libs/battery/MultiStateCounter.h @@ -31,6 +31,8 @@ namespace android { namespace battery { +#define REPORTED_INVALID_TIMESTAMP_DELTA_MS 60000 + typedef uint16_t state_t; template <class T> @@ -171,8 +173,12 @@ void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { if (timestamp >= lastStateChangeTimestamp) { states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp; } else { - ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n", - (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp); + if (timestamp < lastStateChangeTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) { + ALOGE("setState is called with an earlier timestamp: %lu, " + "previous timestamp: %lu\n", + (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp); + } + // The accumulated durations have become unreliable. For example, if the timestamp // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas, // we would get 4000, which is greater than (last - first). This could lead to @@ -232,8 +238,10 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { } } } else if (timestamp < lastUpdateTimestamp) { - ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n", - (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp); + if (timestamp < lastUpdateTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) { + ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n", + (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp); + } for (int i = 0; i < stateCount; i++) { states[i].timeInStateSinceUpdate = 0; diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index 2c25f746a8..196161b3e2 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library_headers { diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp index 6c2a980f71..03ab31e318 100644 --- a/libs/bufferstreams/Android.bp +++ b/libs/bufferstreams/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } aconfig_declarations { diff --git a/libs/bufferstreams/aidl/Android.bp b/libs/bufferstreams/aidl/Android.bp new file mode 100644 index 0000000000..3f1fa4e532 --- /dev/null +++ b/libs/bufferstreams/aidl/Android.bp @@ -0,0 +1,43 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +aidl_interface { + name: "android.graphics.bufferstreams", + unstable: true, + flags: ["-Werror"], + srcs: ["android/graphics/bufferstreams/*.aidl"], + headers: [ + "HardwareBuffer_aidl", + ], + imports: [ + "android.hardware.common-V2", + ], + backend: { + cpp: { + enabled: false, + }, + java: { + enabled: false, + }, + ndk: { + enabled: false, + }, + rust: { + enabled: true, + additional_rustlibs: [ + "libnativewindow_rs", + ], + }, + }, +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl new file mode 100644 index 0000000000..5c905b1d17 --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.graphics.bufferstreams.IBufferOwner; +import android.hardware.HardwareBuffer; + +// Single mapping between a buffer reference and heavy-weight data (like the +// buffer itself) and data that is stable between frames. +parcelable BufferAttachment { + // The HardwareBuffer itself. + // + // This field is @nullable for codegen, since HardwareBuffer doesn't implement Default in Rust. + // In practice, it should never be null. + @nullable HardwareBuffer buffer; + // The buffer owner to which this buffer should be returned. + IBufferOwner owner; +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl new file mode 100644 index 0000000000..75041196a8 --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.graphics.bufferstreams.BufferAttachment; + +// A event that changes the state downstream buffer caches. Clients are responsible for forwarding +// these messages to their clients. +union BufferCacheUpdate { + // Event requiring downstream caches to add new entries. + CacheBuffers cacheBuffers; + // Event requiring downstream caches to remove entries. + ForgetBuffers forgetBuffers; + + parcelable CacheBuffers { + // Attachments to add. + List<BufferAttachment> attachments; + } + + parcelable ForgetBuffers { + // References to remove. + long[] bufferIds; + } +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl new file mode 100644 index 0000000000..1e0ec3b5db --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.os.ParcelFileDescriptor; + +// A Frame represents a single buffer passing through the stream. +parcelable Frame { + // The service must have provided an associated BufferAttachment and the client is required to + // maintain a cache between the two. + long bufferId; + // The expected present time of this frame, or -1 if immediate. + long presentTimeNs; + // The acquire fence of the buffer for this frame. + @nullable ParcelFileDescriptor fence; +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl new file mode 100644 index 0000000000..8b25a6298e --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.os.ParcelFileDescriptor; + +// Interface from a client back to the owner of a buffer. +interface IBufferOwner { + // Called when the buffer is done being processed by the stream to return its owner. + oneway void onBufferReleased(in long bufferId, in @nullable ParcelFileDescriptor releaseFence); +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl new file mode 100644 index 0000000000..52e8216e60 --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.graphics.bufferstreams.BufferCacheUpdate; +import android.graphics.bufferstreams.IBufferSubscription; +import android.graphics.bufferstreams.Frame; + +// Interface provided by clients to a service, mirroring the non-IPC interface. +// +// Clients are required to maintain a local cache of Buffer IDs to BufferAttachments. +interface IBufferSubscriber { + // Provide a BufferSubscription object which the client can use to request frames. + oneway void onSubscribe(in IBufferSubscription subscription); + + // Notifies the client to update its local caches. + oneway void onBufferCacheUpdate(in BufferCacheUpdate update); + + // Notifies the client that a requested frame is available. + oneway void onNext(in Frame frame); + + // Notifies the client that a fatal error has occurred. No subsequent on_next events will be + // sent by the service. + // + // Clients must empty their caches. + oneway void onError(); + + // Notifies the client that no further on_next events will be sent by the service in response + // to it cancelling the subscription. + // + // Clients must empty their caches. + oneway void onComplete(); +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl new file mode 100644 index 0000000000..c37f4e68ea --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +// Interface provided to a IBufferSubscriber to request frames or gracefully cancel their +// subscription. +interface IBufferSubscription { + // Request n more frames. + oneway void request(long n); + // Cancel the subscription. Requested frames may continue to arrive. + oneway void cancel(); +} diff --git a/libs/bufferstreams/examples/app/Android.bp b/libs/bufferstreams/examples/app/Android.bp index bb573c596c..5b3ec30bd5 100644 --- a/libs/bufferstreams/examples/app/Android.bp +++ b/libs/bufferstreams/examples/app/Android.bp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", +} + android_app { name: "BufferStreamsDemoApp", srcs: ["java/**/*.kt"], diff --git a/libs/bufferstreams/examples/app/jni/Android.bp b/libs/bufferstreams/examples/app/jni/Android.bp index 67910a1c4d..003f4ed2ad 100644 --- a/libs/bufferstreams/examples/app/jni/Android.bp +++ b/libs/bufferstreams/examples/app/jni/Android.bp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", +} + cc_library_shared { name: "libbufferstreamdemoapp", cflags: [ diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp index 7fcb222085..34feb5d2a5 100644 --- a/libs/bufferstreams/rust/Android.bp +++ b/libs/bufferstreams/rust/Android.bp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", +} + rust_defaults { name: "libbufferstreams_defaults", srcs: ["src/lib.rs"], diff --git a/libs/bufferstreams/rust/src/buffers/buffer_owner.rs b/libs/bufferstreams/rust/src/buffers/buffer_owner.rs index a4abb9d3b7..155a8bf238 100644 --- a/libs/bufferstreams/rust/src/buffers/buffer_owner.rs +++ b/libs/bufferstreams/rust/src/buffers/buffer_owner.rs @@ -16,7 +16,7 @@ use super::Buffer; /// Trait that represents an owner of a buffer that might need to handle events such as a buffer /// being dropped. -pub trait BufferOwner { +pub trait BufferOwner: Send + Sync { /// Called when a buffer is dropped. fn on_return(&self, buffer: &Buffer); } diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs index be1525d41f..17d4d8767f 100644 --- a/libs/bufferstreams/rust/src/lib.rs +++ b/libs/bufferstreams/rust/src/lib.rs @@ -23,8 +23,6 @@ pub mod subscriptions; use buffers::Buffer; pub use stream_config::*; -use std::time::Instant; - /// This function will print Hello World. #[no_mangle] pub extern "C" fn hello() -> bool { @@ -106,7 +104,8 @@ pub trait BufferSubscriber { /// BufferSubscriptions serve as the bridge between BufferPublishers and /// BufferSubscribers. BufferSubscribers receive a BufferSubscription when they /// subscribe to a BufferPublisher via on_subscribe. -/// This object is to be used by the BufferSubscriber to cancel its subscription +/// +/// This object is used by the BufferSubscriber to cancel its subscription /// or request more buffers. /// /// BufferSubcriptions are required to adhere to the following, based on the @@ -147,7 +146,7 @@ pub trait BufferSubscriber { /// no other Subscription exists at this point. /// * Calling Subscription.cancel MUST return normally. /// * Calling Subscription.request MUST return normally. -pub trait BufferSubscription { +pub trait BufferSubscription: Send + Sync + 'static { /// request fn request(&self, n: u64); /// cancel @@ -161,8 +160,8 @@ pub type BufferError = anyhow::Error; pub struct Frame { /// A buffer to be used this frame. pub buffer: Buffer, - /// The time at which the buffer was dispatched. - pub present_time: Instant, + /// The time at which this buffer is expected to be displayed. + pub present_time: i64, /// A fence used for reading/writing safely. pub fence: i32, } @@ -175,14 +174,12 @@ mod test { use anyhow::anyhow; use buffers::Buffer; use nativewindow::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags}; - use std::borrow::BorrowMut; - use std::error::Error; - use std::ops::Add; - use std::sync::Arc; - use std::time::Duration; + use std::{borrow::BorrowMut, error::Error, ops::Add, sync::Arc}; - use crate::publishers::testing::*; - use crate::subscribers::{testing::*, SharedSubscriber}; + use crate::{ + publishers::testing::*, + subscribers::{testing::*, SharedSubscriber}, + }; const STREAM_CONFIG: StreamConfig = StreamConfig { width: 1, @@ -200,7 +197,7 @@ mod test { .create_hardware_buffer() .expect("Unable to create hardware buffer for test"), ), - present_time: Instant::now() + Duration::from_secs(1), + present_time: 1, fence: 0, } } diff --git a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs index c5c1fd37c1..82f528e831 100644 --- a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs +++ b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::time::Instant; - use crate::{ buffers::BufferPool, subscriptions::SharedBufferSubscription, BufferPublisher, BufferSubscriber, Frame, StreamConfig, @@ -41,7 +39,7 @@ impl BufferPoolPublisher { /// If the [SharedBufferSubscription] is ready for a [Frame], a buffer will be requested from /// [BufferPool] and sent over to the [BufferSubscriber]. - pub fn send_next_frame(&mut self, present_time: Instant) -> bool { + pub fn send_next_frame(&mut self, present_time: i64) -> bool { if let Some(subscriber) = self.subscriber.as_mut() { if self.subscription.take_request() { if let Some(buffer) = self.buffer_pool.next_buffer() { @@ -101,7 +99,7 @@ mod test { subscriber.map_inner(|s| s.request(1)); - assert!(buffer_pool_publisher.send_next_frame(Instant::now())); + assert!(buffer_pool_publisher.send_next_frame(1)); let events = subscriber.map_inner_mut(|s| s.take_events()); assert!(matches!(events.last().unwrap(), TestingSubscriberEvent::Next(_))); diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 918680d6a7..32b2b68b4d 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -5,16 +5,21 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { name: "ftl_test", test_suites: ["device-tests"], + header_libs: [ + "libbase_headers", + ], srcs: [ "algorithm_test.cpp", "cast_test.cpp", "concat_test.cpp", "enum_test.cpp", + "expected_test.cpp", "fake_guard_test.cpp", "flags_test.cpp", "function_test.cpp", diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp new file mode 100644 index 0000000000..8cb07e4696 --- /dev/null +++ b/libs/ftl/expected_test.cpp @@ -0,0 +1,77 @@ +/* + * 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 <ftl/expected.h> +#include <gtest/gtest.h> + +#include <string> +#include <system_error> + +namespace android::test { + +using IntExp = ftl::Expected<int, std::errc>; +using StringExp = ftl::Expected<std::string, std::errc>; + +using namespace std::string_literals; + +TEST(Expected, Construct) { + // Default value. + EXPECT_TRUE(IntExp().has_value()); + EXPECT_EQ(IntExp(), IntExp(0)); + + EXPECT_TRUE(StringExp().has_value()); + EXPECT_EQ(StringExp(), StringExp("")); + + // Value. + ASSERT_TRUE(IntExp(42).has_value()); + EXPECT_EQ(42, IntExp(42).value()); + + ASSERT_TRUE(StringExp("test").has_value()); + EXPECT_EQ("test"s, StringExp("test").value()); + + // Error. + const auto exp = StringExp(ftl::Unexpected(std::errc::invalid_argument)); + ASSERT_FALSE(exp.has_value()); + EXPECT_EQ(std::errc::invalid_argument, exp.error()); +} + +TEST(Expected, HasError) { + EXPECT_FALSE(IntExp(123).has_error([](auto) { return true; })); + EXPECT_FALSE(IntExp(ftl::Unexpected(std::errc::io_error)).has_error([](auto) { return false; })); + + EXPECT_TRUE(StringExp(ftl::Unexpected(std::errc::permission_denied)).has_error([](auto e) { + return e == std::errc::permission_denied; + })); +} + +TEST(Expected, ValueOpt) { + EXPECT_EQ(ftl::Optional(-1), IntExp(-1).value_opt()); + EXPECT_EQ(std::nullopt, IntExp(ftl::Unexpected(std::errc::broken_pipe)).value_opt()); + + { + const StringExp exp("foo"s); + EXPECT_EQ(ftl::Optional('f'), + exp.value_opt().transform([](const auto& s) { return s.front(); })); + EXPECT_EQ("foo"s, exp.value()); + } + { + StringExp exp("foobar"s); + EXPECT_EQ(ftl::Optional(6), std::move(exp).value_opt().transform(&std::string::length)); + EXPECT_TRUE(exp.value().empty()); + } +} + +} // namespace android::test diff --git a/libs/ftl/future_test.cpp b/libs/ftl/future_test.cpp index 5a245b681c..1140639c87 100644 --- a/libs/ftl/future_test.cpp +++ b/libs/ftl/future_test.cpp @@ -102,4 +102,42 @@ TEST(Future, Chain) { decrement_thread.join(); } +TEST(Future, WaitFor) { + using namespace std::chrono_literals; + { + auto future = ftl::yield(42); + // Check that we can wait_for multiple times without invalidating the future + EXPECT_EQ(future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(future.get(), 42); + } + + { + std::condition_variable cv; + std::mutex m; + bool ready = false; + + std::packaged_task<int32_t()> get_int([&] { + std::unique_lock lk(m); + cv.wait(lk, [&] { return ready; }); + return 24; + }); + + auto get_future = ftl::Future(get_int.get_future()); + std::thread get_thread(std::move(get_int)); + + EXPECT_EQ(get_future.wait_for(0s), std::future_status::timeout); + { + std::unique_lock lk(m); + ready = true; + } + cv.notify_one(); + + EXPECT_EQ(get_future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(get_future.get(), 24); + + get_thread.join(); + } +} + } // namespace android::test diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp index 634877f672..e96d70d8ad 100644 --- a/libs/ftl/small_map_test.cpp +++ b/libs/ftl/small_map_test.cpp @@ -189,9 +189,20 @@ TEST(SmallMap, Get) { } } -TEST(SmallMap, TryEmplace) { - SmallMap<int, std::string, 3> map; - using Pair = decltype(map)::value_type; +template <typename Capacity> +struct SmallMapTest : testing::Test { + static constexpr std::size_t kCapacity = Capacity{}(); +}; + +template <std::size_t N> +using Capacity = std::integral_constant<std::size_t, N>; + +using Capacities = testing::Types<Capacity<3>, Capacity<0>>; +TYPED_TEST_SUITE(SmallMapTest, Capacities, ); + +TYPED_TEST(SmallMapTest, TryEmplace) { + SmallMap<int, std::string, TestFixture::kCapacity> map; + using Pair = typename decltype(map)::value_type; { const auto [it, ok] = map.try_emplace(123, "abc"); @@ -207,14 +218,22 @@ TEST(SmallMap, TryEmplace) { const auto [it, ok] = map.try_emplace(-1); ASSERT_TRUE(ok); EXPECT_EQ(*it, Pair(-1, std::string())); - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } } { // Insertion fails if mapping exists. const auto [it, ok] = map.try_emplace(42, "!!!"); EXPECT_FALSE(ok); EXPECT_EQ(*it, Pair(42, "???")); - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } } { // Insertion at capacity promotes the map. @@ -240,9 +259,9 @@ struct String { } // namespace -TEST(SmallMap, TryReplace) { - SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); - using Pair = decltype(map)::value_type; +TYPED_TEST(SmallMapTest, TryReplace) { + SmallMap<int, String, TestFixture::kCapacity> map = ftl::init::map(1, "a")(2, "B"); + using Pair = typename decltype(map)::value_type; { // Replacing fails unless mapping exists. @@ -260,7 +279,12 @@ TEST(SmallMap, TryReplace) { EXPECT_EQ(*it, Pair(2, "b")); } - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } + EXPECT_TRUE(map.try_emplace(3, "abc").second); EXPECT_TRUE(map.try_emplace(4, "d").second); EXPECT_TRUE(map.dynamic()); @@ -284,9 +308,9 @@ TEST(SmallMap, TryReplace) { EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); } -TEST(SmallMap, EmplaceOrReplace) { - SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); - using Pair = decltype(map)::value_type; +TYPED_TEST(SmallMapTest, EmplaceOrReplace) { + SmallMap<int, String, TestFixture::kCapacity> map = ftl::init::map(1, "a")(2, "B"); + using Pair = typename decltype(map)::value_type; { // New mapping is emplaced. @@ -305,7 +329,12 @@ TEST(SmallMap, EmplaceOrReplace) { EXPECT_EQ(*it, Pair(2, "b")); } - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } + EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace. EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace. EXPECT_TRUE(map.dynamic()); diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index 6e2b803174..8dabc2c79e 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library { diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp index 3c3b6af670..833718213a 100644 --- a/libs/gralloc/types/fuzzer/Android.bp +++ b/libs/gralloc/types/fuzzer/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_fuzz { diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp index 66eb0aa2fe..b796c03d6a 100644 --- a/libs/gralloc/types/tests/Android.bp +++ b/libs/gralloc/types/tests/Android.bp @@ -21,6 +21,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { @@ -30,5 +31,8 @@ cc_test { "libhidlbase", ], srcs: ["Gralloc4_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 059e19ebb8..b693e44ee4 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -18,6 +18,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } aconfig_declarations { diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 19693e37cf..fb69fda32d 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -887,6 +887,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, int callbackTicket = 0; uint64_t currentFrameNumber = 0; BufferItem item; + int connectedApi; + sp<Fence> lastQueuedFence; + { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); @@ -1056,6 +1059,13 @@ status_t BufferQueueProducer::queueBuffer(int slot, callbackTicket = mNextCallbackTicket++; VALIDATE_CONSISTENCY(); + + connectedApi = mCore->mConnectedApi; + lastQueuedFence = std::move(mLastQueueBufferFence); + + mLastQueueBufferFence = std::move(acquireFence); + mLastQueuedCrop = item.mCrop; + mLastQueuedTransform = item.mTransform; } // Autolock scope // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because @@ -1079,9 +1089,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order - int connectedApi; - sp<Fence> lastQueuedFence; - { // scope for the lock std::unique_lock<std::mutex> lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { @@ -1094,13 +1101,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, frameReplacedListener->onFrameReplaced(item); } - connectedApi = mCore->mConnectedApi; - lastQueuedFence = std::move(mLastQueueBufferFence); - - mLastQueueBufferFence = std::move(acquireFence); - mLastQueuedCrop = item.mCrop; - mLastQueuedTransform = item.mTransform; - ++mCurrentCallbackTicket; mCallbackCondition.notify_all(); } @@ -1653,9 +1653,10 @@ status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) { status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); - BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); + BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); + if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; @@ -1679,10 +1680,11 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect, uint32_t* outTransform) { ATRACE_CALL(); - BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); - if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { + BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); + if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT || + mSlots[mCore->mLastQueuedSlot].mBufferState.isDequeued()) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; return NO_ERROR; diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 79c5fd1006..4518b67d4c 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -344,6 +344,13 @@ void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { handleRefreshRateUpdates(); } +void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) { + ALOGV("choreographer %p ~ received hdcp levels change event (displayId=%s, connectedLevel=%d, " + "maxLevel=%d), ignoring.", + this, to_string(displayId).c_str(), connectedLevel, maxLevel); +} + void Choreographer::handleMessage(const Message& message) { switch (message.what) { case MSG_SCHEDULE_CALLBACKS: diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 5dd058cf9f..f3de96d2cd 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -195,6 +195,11 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId, std::move(mFrameRateOverrides)); break; + case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: + dispatchHdcpLevelsChanged(ev.header.displayId, + ev.hdcpLevelsChange.connectedLevel, + ev.hdcpLevelsChange.maxLevel); + break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); break; diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp index 11524e2b51..01aa7ed43c 100644 --- a/libs/gui/FrameRateUtils.cpp +++ b/libs/gui/FrameRateUtils.cpp @@ -42,6 +42,7 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrame if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE && + compatibility != ANATIVEWINDOW_FRAME_RATE_GTE && (!privileged || (compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT && compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 38fab9cdaa..1e0aacddab 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -199,7 +199,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeParcelable, trustedPresentationListener); SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio); SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio); - SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)) + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)); return NO_ERROR; } @@ -484,6 +484,12 @@ void layer_state_t::sanitize(int32_t permissions) { flags &= ~eLayerIsDisplayDecoration; ALOGE("Stripped attempt to set LayerIsDisplayDecoration in sanitize"); } + if ((mask & eCanOccludePresentation) && + !(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + flags &= ~eCanOccludePresentation; + mask &= ~eCanOccludePresentation; + ALOGE("Stripped attempt to set eCanOccludePresentation in sanitize"); + } } if (what & layer_state_t::eInputInfoChanged) { @@ -605,6 +611,10 @@ void layer_state_t::merge(const layer_state_t& other) { desiredHdrSdrRatio = other.desiredHdrSdrRatio; currentHdrSdrRatio = other.currentHdrSdrRatio; } + if (other.what & eDesiredHdrHeadroomChanged) { + what |= eDesiredHdrHeadroomChanged; + desiredHdrSdrRatio = other.desiredHdrSdrRatio; + } if (other.what & eCachingHintChanged) { what |= eCachingHintChanged; cachingHint = other.cachingHint; @@ -768,6 +778,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eDataspaceChanged, other, dataspace); CHECK_DIFF2(diff, eExtendedRangeBrightnessChanged, other, currentHdrSdrRatio, desiredHdrSdrRatio); + CHECK_DIFF(diff, eDesiredHdrHeadroomChanged, other, desiredHdrSdrRatio); CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint); CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata); if (other.what & eSurfaceDamageRegionChanged && diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 07a0cfed63..086544e48a 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -342,12 +342,23 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, getFrameTimestamp(outRequestedPresentTime, events->requestedPresentTime); getFrameTimestamp(outLatchTime, events->latchTime); - getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime); + + nsecs_t firstRefreshStartTime = NATIVE_WINDOW_TIMESTAMP_INVALID; + getFrameTimestamp(&firstRefreshStartTime, events->firstRefreshStartTime); + if (outFirstRefreshStartTime) { + *outFirstRefreshStartTime = firstRefreshStartTime; + } + getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime); getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime); - getFrameTimestampFence(outAcquireTime, events->acquireFence, + nsecs_t acquireTime = NATIVE_WINDOW_TIMESTAMP_INVALID; + getFrameTimestampFence(&acquireTime, events->acquireFence, events->hasAcquireInfo()); + if (outAcquireTime != nullptr) { + *outAcquireTime = acquireTime; + } + getFrameTimestampFence(outGpuCompositionDoneTime, events->gpuCompositionDoneFence, events->hasGpuCompositionDoneInfo()); @@ -356,6 +367,16 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, getFrameTimestampFence(outReleaseTime, events->releaseFence, events->hasReleaseInfo()); + // Fix up the GPU completion fence at this layer -- eglGetFrameTimestampsANDROID() expects + // that EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID > EGL_RENDERING_COMPLETE_TIME_ANDROID. + // This is typically true, but SurfaceFlinger may opt to cache prior GPU composition results, + // which breaks that assumption, so zero out GPU composition time. + if (outGpuCompositionDoneTime != nullptr + && *outGpuCompositionDoneTime > 0 && (acquireTime > 0 || firstRefreshStartTime > 0) + && *outGpuCompositionDoneTime <= std::max(acquireTime, firstRefreshStartTime)) { + *outGpuCompositionDoneTime = 0; + } + return NO_ERROR; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 079ccda0fb..7f64a04347 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -56,6 +56,7 @@ #include <android-base/thread_annotations.h> #include <gui/LayerStatePermissions.h> +#include <gui/ScreenCaptureResults.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> @@ -1807,6 +1808,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setExten return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredHdrHeadroom( + const sp<SurfaceControl>& sc, float desiredRatio) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eDesiredHdrHeadroomChanged; + s->desiredHdrSdrRatio = desiredRatio; + + registerSurfaceControlForCallback(sc); + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachingHint( const sp<SurfaceControl>& sc, gui::CachingHint cachingHint) { layer_state_t* s = getLayerState(sc); @@ -3116,7 +3131,6 @@ status_t SurfaceComposerClient::removeWindowInfosListener( ->removeWindowInfosListener(windowInfosListener, ComposerServiceAIDL::getComposerService()); } - // ---------------------------------------------------------------------------- status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, @@ -3138,11 +3152,19 @@ status_t ScreenshotClient::captureDisplay(DisplayId displayId, const gui::Captur } status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, - const sp<IScreenCaptureListener>& captureListener) { + const sp<IScreenCaptureListener>& captureListener, + bool sync) { sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService()); if (s == nullptr) return NO_INIT; - binder::Status status = s->captureLayers(captureArgs, captureListener); + binder::Status status; + if (sync) { + gui::ScreenCaptureResults captureResults; + status = s->captureLayersSync(captureArgs, &captureResults); + captureListener->onScreenCaptureCompleted(captureResults); + } else { + status = s->captureLayers(captureArgs, captureListener); + } return statusTFromBinderStatus(status); } diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index ba1d196e9c..86bf0ee745 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -109,7 +109,8 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.inputConfig == inputConfig && info.displayId == displayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType && - info.layoutParamsFlags == layoutParamsFlags; + info.layoutParamsFlags == layoutParamsFlags && + info.canOccludePresentation == canOccludePresentation; } status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { @@ -158,8 +159,9 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->write(touchableRegion) ?: parcel->writeBool(replaceTouchableRegionWithCrop) ?: parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: - parcel->writeStrongBinder(windowToken); - parcel->writeStrongBinder(focusTransferTarget); + parcel->writeStrongBinder(windowToken) ?: + parcel->writeStrongBinder(focusTransferTarget) ?: + parcel->writeBool(canOccludePresentation); // clang-format on return status; } @@ -210,7 +212,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readBool(&replaceTouchableRegionWithCrop) ?: parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: parcel->readNullableStrongBinder(&windowToken) ?: - parcel->readNullableStrongBinder(&focusTransferTarget); + parcel->readNullableStrongBinder(&focusTransferTarget) ?: + parcel->readBool(&canOccludePresentation); // clang-format on @@ -258,10 +261,7 @@ void WindowInfoHandle::updateFrom(sp<WindowInfoHandle> handle) { mInfo = handle->mInfo; } -std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) { - const WindowInfo& info = *window.getInfo(); - std::string transform; - info.transform.dump(transform, "transform", " "); +std::ostream& operator<<(std::ostream& out, const WindowInfo& info) { out << "name=" << info.name << ", id=" << info.id << ", displayId=" << info.displayId << ", inputConfig=" << info.inputConfig.string() << ", alpha=" << info.alpha << ", frame=[" << info.frame.left << "," << info.frame.top << "][" << info.frame.right << "," @@ -272,8 +272,17 @@ std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) { << ", ownerUid=" << info.ownerUid.toString() << ", dispatchingTimeout=" << std::chrono::duration_cast<std::chrono::milliseconds>(info.dispatchingTimeout).count() << "ms, token=" << info.token.get() - << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode) << "\n" - << transform; + << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode); + if (info.canOccludePresentation) out << ", canOccludePresentation"; + std::string transform; + info.transform.dump(transform, "transform", " "); + out << "\n" << transform; + return out; +} + +std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) { + const WindowInfo& info = *window.getInfo(); + out << info; return out; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index e3122bc300..51e01930d3 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -46,6 +46,7 @@ import android.gui.LayerCaptureArgs; import android.gui.LayerDebugInfo; import android.gui.OverlayProperties; import android.gui.PullAtomData; +import android.gui.ScreenCaptureResults; import android.gui.ARect; import android.gui.SchedulingPolicy; import android.gui.StalledTransactionInfo; @@ -245,6 +246,16 @@ interface ISurfaceComposer { /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. * This requires READ_FRAME_BUFFER permission. This function will fail if there + * is a secure window on screen. This is a blocking call and will return the + * ScreenCaptureResults, including the captured buffer. Because this is blocking, the + * caller doesn't care about the fence and the binder thread in SurfaceFlinger will wait + * on the fence to fire before returning the results. + */ + ScreenCaptureResults captureLayersSync(in LayerCaptureArgs args); + + /** + * Capture a subtree of the layer hierarchy, potentially ignoring the root node. + * This requires READ_FRAME_BUFFER permission. This function will fail if there * is a secure window on screen */ oneway void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener); diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp deleted file mode 100644 index cd738acde2..0000000000 --- a/libs/gui/fuzzer/Android.bp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2021 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_defaults { - name: "libgui_fuzzer_defaults", - defaults: ["android.hardware.power-ndk_shared"], - static_libs: [ - "android.hidl.token@1.0-utils", - "libbinder_random_parcel", - "libgui_aidl_static", - "libgui_window_info_static", - "libpdx", - "libgmock", - "libgui_mocks", - "libgmock_ndk", - "libgmock_main", - "libgtest_ndk_c++", - "libgmock_main_ndk", - "librenderengine_mocks", - "perfetto_trace_protos", - "libcompositionengine_mocks", - "perfetto_trace_protos", - ], - shared_libs: [ - "android.hardware.configstore@1.0", - "android.hardware.configstore-utils", - "android.hardware.graphics.bufferqueue@1.0", - "android.hardware.graphics.bufferqueue@2.0", - "android.hidl.token@1.0", - "libSurfaceFlingerProp", - "libgui", - "libbase", - "liblog", - "libEGL", - "libGLESv2", - "libbinder", - "libcutils", - "libhidlbase", - "libinput", - "libui", - "libutils", - "libnativewindow", - "libvndksupport", - ], - header_libs: [ - "libdvr_headers", - "libui_fuzzableDataspaces_headers", - ], - fuzz_config: { - cc: [ - "android-media-fuzzing-reports@google.com", - ], - componentid: 155276, - hotlists: [ - "4593311", - ], - description: "The fuzzer targets the APIs of libgui library", - vector: "local_no_privileges_required", - service_privilege: "privileged", - users: "multi_user", - fuzzed_code_usage: "shipped", - }, -} - -cc_fuzz { - name: "libgui_surfaceComposer_fuzzer", - srcs: [ - "libgui_surfaceComposer_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - "service_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_surfaceComposerClient_fuzzer", - srcs: [ - "libgui_surfaceComposerClient_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - "service_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_parcelable_fuzzer", - srcs: [ - "libgui_parcelable_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_bufferQueue_fuzzer", - srcs: [ - "libgui_bufferQueue_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_consumer_fuzzer", - srcs: [ - "libgui_consumer_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_displayEvent_fuzzer", - srcs: [ - "libgui_displayEvent_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - ], -} diff --git a/libs/gui/fuzzer/README.md b/libs/gui/fuzzer/README.md deleted file mode 100644 index 96e27c989f..0000000000 --- a/libs/gui/fuzzer/README.md +++ /dev/null @@ -1,219 +0,0 @@ -# Fuzzers for Libgui - -## Table of contents -+ [libgui_surfaceComposer_fuzzer](#SurfaceComposer) -+ [libgui_surfaceComposerClient_fuzzer](#SurfaceComposerClient) -+ [libgui_parcelable_fuzzer](#Libgui_Parcelable) -+ [libgui_bufferQueue_fuzzer](#BufferQueue) -+ [libgui_consumer_fuzzer](#Libgui_Consumer) -+ [libgui_displayEvent_fuzzer](#LibGui_DisplayEvent) - -# <a name="libgui_surfaceComposer_fuzzer"></a> Fuzzer for SurfaceComposer - -SurfaceComposer supports the following parameters: -1. SurfaceWidth (parameter name:`width`) -2. SurfaceHeight (parameter name:`height`) -3. TransactionStateFlags (parameter name:`flags`) -4. TransformHint (parameter name:`outTransformHint`) -5. SurfacePixelFormat (parameter name:`format`) -6. LayerId (parameter name:`outLayerId`) -7. SurfaceComposerTags (parameter name:`surfaceTag`) -8. PowerBoostID (parameter name:`boostId`) -9. VsyncSource (parameter name:`vsyncSource`) -10. EventRegistrationFlags (parameter name:`eventRegistration`) -11. FrameRateCompatibility (parameter name:`frameRateCompatibility`) -12. ChangeFrameRateStrategy (parameter name:`changeFrameRateStrategy`) -13. HdrTypes (parameter name:`hdrTypes`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`surfaceTag` | 0.`BnSurfaceComposer::BOOT_FINISHED`, 1.`BnSurfaceComposer::CREATE_CONNECTION`, 2.`BnSurfaceComposer::GET_STATIC_DISPLAY_INFO`, 3.`BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION`, 4.`BnSurfaceComposer::CREATE_DISPLAY`, 5.`BnSurfaceComposer::DESTROY_DISPLAY`, 6.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN`, 7.`BnSurfaceComposer::SET_TRANSACTION_STATE`, 8.`BnSurfaceComposer::AUTHENTICATE_SURFACE`, 9.`BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS`, 10.`BnSurfaceComposer::GET_DISPLAY_STATE`, 11.`BnSurfaceComposer::CAPTURE_DISPLAY`, 12.`BnSurfaceComposer::CAPTURE_LAYERS`, 13.`BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS`, 14.`BnSurfaceComposer::GET_ANIMATION_FRAME_STATS`, 15.`BnSurfaceComposer::SET_POWER_MODE`, 16.`BnSurfaceComposer::GET_DISPLAY_STATS`, 17.`BnSurfaceComposer::SET_ACTIVE_COLOR_MODE`, 18.`BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS`, 19.`BnSurfaceComposer::INJECT_VSYNC`, 20.`BnSurfaceComposer::GET_LAYER_DEBUG_INFO`, 21.`BnSurfaceComposer::GET_COMPOSITION_PREFERENCE`, 22.`BnSurfaceComposer::GET_COLOR_MANAGEMENT`, 23.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES`, 24.`BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED`, 25.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE`, 26.`BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT`, 27.`BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY`, 28.`BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES`, 29.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS`, 30.`BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER`, 31.`BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER`, 32.`BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS`, 33.`BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS`, 34.`BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT`, 35.`BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS`, 36.`BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID`, 37.`BnSurfaceComposer::NOTIFY_POWER_BOOST`, 38.`BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS`, 39.`BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE`, 40.`BnSurfaceComposer::SET_GAME_CONTENT_TYPE`, 41.`BnSurfaceComposer::SET_FRAME_RATE`, 42.`BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN`, 43.`BnSurfaceComposer::SET_FRAME_TIMELINE_INFO`, 44.`BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER`, 45.`BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY`, 46.`BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT`, 47.`BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO`, 48.`BnSurfaceComposer::ADD_FPS_LISTENER`, 49.`BnSurfaceComposer::REMOVE_FPS_LISTENER`, 50.`BnSurfaceComposer::OVERRIDE_HDR_TYPES`, 51.`BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER`, 52.`BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER`, 53.`BnSurfaceComposer::ON_PULL_ATOM`, 54.`BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER`, 55.`BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER` | Value obtained from FuzzedDataProvider| -|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider| -|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider| -|`eventRegistration`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride` |Value obtained from FuzzedDataProvider| -|`frameRateCompatibility`| 0.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT`, 1.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE` |Value obtained from FuzzedDataProvider| -|`changeFrameRateStrategy`| 0.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS`, 1.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS` |Value obtained from FuzzedDataProvider| -|`hdrTypes`| 0.`ui::Hdr::DOLBY_VISION`, 1.`ui::Hdr::HDR10`, 2.`ui::Hdr::HLG`, 3.`ui::Hdr::HDR10_PLUS` |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_surfaceComposer_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_surfaceComposer_fuzzer/libgui_surfaceComposer_fuzzer -``` - -# <a name="libgui_surfaceComposerClient_fuzzer"></a> Fuzzer for SurfaceComposerClient - -SurfaceComposerClient supports the following data sources: -1. SurfaceWidth (parameter name:`width`) -2. SurfaceHeight (parameter name:`height`) -3. TransactionStateFlags (parameter name:`flags`) -4. TransformHint (parameter name:`outTransformHint`) -5. SurfacePixelFormat (parameter name:`format`) -6. LayerId (parameter name:`outLayerId`) -7. SurfaceComposerClientTags (parameter name:`surfaceTag`) -8. DefaultMode (parameter name:`defaultMode`) -9. PrimaryRefreshRateMin (parameter name:`primaryRefreshRateMin`) -10. PrimaryRefreshRateMax (parameter name:`primaryRefreshRateMax`) -11. AppRefreshRateMin (parameter name:`appRefreshRateMin`) -12. AppRefreshRateMax (parameter name:`appRefreshRateMax`) -13. DisplayPowerMode (parameter name:`mode`) -14. CacheId (parameter name:`cacheId`) -15. DisplayBrightness (parameter name:`brightness`) -16. PowerBoostID (parameter name:`boostId`) -17. AtomId (parameter name:`atomId`) -18. ComponentMask (parameter name:`componentMask`) -19. MaxFrames (parameter name:`maxFrames`) -20. TaskId (parameter name:`taskId`) -21. Alpha (parameter name:`aplha`) -22. CornerRadius (parameter name:`cornerRadius`) -23. BackgroundBlurRadius (parameter name:`backgroundBlurRadius`) -24. Half3Color (parameter name:`color`) -25. LayerStack (parameter name:`layerStack`) -26. Dataspace (parameter name:`dataspace`) -27. Api (parameter name:`api`) -28. Priority (parameter name:`priority`) -29. TouchableRegionPointX (parameter name:`pointX`) -30. TouchableRegionPointY (parameter name:`pointY`) -31. ColorMode (parameter name:`colorMode`) -32. WindowInfoFlags (parameter name:`flags`) -33. WindowInfoTransformOrientation (parameter name:`transform`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`surfaceTag`| 0.`Tag::CREATE_SURFACE`, 1.`Tag::CREATE_WITH_SURFACE_PARENT`, 2.`Tag::CLEAR_LAYER_FRAME_STATS`, 3.`Tag::GET_LAYER_FRAME_STATS`, 4.`Tag::MIRROR_SURFACE`, 5.`Tag::LAST` |Value obtained from FuzzedDataProvider| -|`mode`| 0.`gui::TouchOcclusionMode::BLOCK_UNTRUSTED`, 1.`gui::TouchOcclusionMode::USE_OPACITY`, 2.`gui::TouchOcclusionMode::ALLOW` |Value obtained from FuzzedDataProvider| -|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider| -|`colorMode`|0.`ui::ColorMode::NATIVE`, 1.`ui::ColorMode::STANDARD_BT601_625`, 2.`ui::ColorMode::STANDARD_BT601_625_UNADJUSTED`, 3.`ui::ColorMode::STANDARD_BT601_525`, 4.`ui::ColorMode::STANDARD_BT601_525_UNADJUSTED`, 5.`ui::ColorMode::STANDARD_BT709`, 6.`ui::ColorMode::DCI_P3`, 7.`ui::ColorMode::SRGB`, 8.`ui::ColorMode::ADOBE_RGB`, 9.`ui::ColorMode::DISPLAY_P3`, 10.`ui::ColorMode::BT2020`, 11.`ui::ColorMode::BT2100_PQ`, 12.`ui::ColorMode::BT2100_HLG`, 13.`ui::ColorMode::DISPLAY_BT2020` |Value obtained from FuzzedDataProvider| -|`flags`|0 .`gui::WindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON`, 1.`gui::WindowInfo::Flag::DIM_BEHIND`, 2.`gui::WindowInfo::Flag::BLUR_BEHIND`, 3.`gui::WindowInfo::Flag::NOT_FOCUSABLE`, 4.`gui::WindowInfo::Flag::NOT_TOUCHABLE`, 5.`gui::WindowInfo::Flag::NOT_TOUCH_MODAL`, 6.`gui::WindowInfo::Flag::TOUCHABLE_WHEN_WAKING`, 7.`gui::WindowInfo::Flag::KEEP_SCREEN_ON`, 8.`gui::WindowInfo::Flag::LAYOUT_IN_SCREEN`, 9.`gui::WindowInfo::Flag::LAYOUT_NO_LIMITS`, 10.`gui::WindowInfo::Flag::FULLSCREEN`, 11.`gui::WindowInfo::Flag::FORCE_NOT_FULLSCREEN`, 12.`gui::WindowInfo::Flag::DITHER`, 13.`gui::WindowInfo::Flag::SECURE`, 14.`gui::WindowInfo::Flag::SCALED`, 15.`gui::WindowInfo::Flag::IGNORE_CHEEK_PRESSES`, 16.`gui::WindowInfo::Flag::LAYOUT_INSET_DECOR`, 17.`gui::WindowInfo::Flag::ALT_FOCUSABLE_IM`, 18.`gui::WindowInfo::Flag::WATCH_OUTSIDE_TOUCH`, 19.`gui::WindowInfo::Flag::SHOW_WHEN_LOCKED`, 20.`gui::WindowInfo::Flag::SHOW_WALLPAPER`, 21.`gui::WindowInfo::Flag::TURN_SCREEN_ON`, 22.`gui::WindowInfo::Flag::DISMISS_KEYGUARD`, 23.`gui::WindowInfo::Flag::SPLIT_TOUCH`, 24.`gui::WindowInfo::Flag::HARDWARE_ACCELERATED`, 25.`gui::WindowInfo::Flag::LAYOUT_IN_OVERSCAN`, 26.`gui::WindowInfo::Flag::TRANSLUCENT_STATUS`, 27.`gui::WindowInfo::Flag::TRANSLUCENT_NAVIGATION`, 28.`gui::WindowInfo::Flag::LOCAL_FOCUS_MODE`, 29.`gui::WindowInfo::Flag::SLIPPERY`, 30.`gui::WindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR`, 31.`gui::WindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS`, |Value obtained from FuzzedDataProvider| -|`dataspace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| -|`transform`| 0.`ui::Transform::ROT_0`, 1.`ui::Transform::FLIP_H`, 2.`ui::Transform::FLIP_V`, 3.`ui::Transform::ROT_90`, 4.`ui::Transform::ROT_180`, 5.`ui::Transform::ROT_270` |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_surfaceComposerClient_fuzzer -``` -2. To run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_surfaceComposerClient_fuzzer/libgui_surfaceComposerClient_fuzzer -``` - -# <a name="libgui_parcelable_fuzzer"></a> Fuzzer for Libgui_Parcelable - -Libgui_Parcelable supports the following parameters: -1. LayerMetadataKey (parameter name:`key`) -2. Dataspace (parameter name:`mDataspace`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`key`| 0.`view::LayerMetadataKey::METADATA_OWNER_UID`, 1.`view::LayerMetadataKey::METADATA_WINDOW_TYPE`, 2.`view::LayerMetadataKey::METADATA_TASK_ID`, 3.`view::LayerMetadataKey::METADATA_MOUSE_CURSOR`, 4.`view::LayerMetadataKey::METADATA_ACCESSIBILITY_ID`, 5.`view::LayerMetadataKey::METADATA_OWNER_PID`, 6.`view::LayerMetadataKey::METADATA_DEQUEUE_TIME`, 7.`view::LayerMetadataKey::METADATA_GAME_MODE`, |Value obtained from FuzzedDataProvider| -|`mDataSpace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_fuzzer/libgui_fuzzer -``` - -# <a name="libgui_bufferQueue_fuzzer"></a> Fuzzer for BufferQueue - -BufferQueue supports the following parameters: -1. SurfaceWidth (parameter name:`width`) -2. SurfaceHeight (parameter name:`height`) -3. TransactionStateFlags (parameter name:`flags`) -4. TransformHint (parameter name:`outTransformHint`) -5. SurfacePixelFormat (parameter name:`format`) -6. LayerId (parameter name:`layerId`) -7. BufferId (parameter name:`bufferId`) -8. FrameNumber (parameter name:`frameNumber`) -9. FrameRate (parameter name:`frameRate`) -10. Compatability (parameter name:`compatability`) -11. LatchTime (parameter name:`latchTime`) -12. AcquireTime (parameter name:`acquireTime`) -13. RefreshTime (parameter name:`refreshTime`) -14. DequeueTime (parameter name:`dequeueTime`) -15. Slot (parameter name:`slot`) -16. MaxBuffers (parameter name:`maxBuffers`) -17. GenerationNumber (parameter name:`generationNumber`) -18. Api (parameter name:`api`) -19. Usage (parameter name:`usage`) -20. MaxFrameNumber (parameter name:`maxFrameNumber`) -21. BufferCount (parameter name:`bufferCount`) -22. MaxAcquredBufferCount (parameter name:`maxAcquredBufferCount`) -23. Status (parameter name:`status`) -24. ApiConnection (parameter name:`apiConnection`) -25. Dataspace (parameter name:`dataspace`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`status`| 0.`OK`, 1.`NO_MEMORY`, 2.`NO_INIT`, 3.`BAD_VALUE`, 4.`DEAD_OBJECT`, 5.`INVALID_OPERATION`, 6.`TIMED_OUT`, 7.`WOULD_BLOCK`, 8.`UNKNOWN_ERROR`, 9.`ALREADY_EXISTS`, |Value obtained from FuzzedDataProvider| -|`apiConnection`| 0.`BufferQueueCore::CURRENTLY_CONNECTED_API`, 1.`BufferQueueCore::NO_CONNECTED_API`, 2.`NATIVE_WINDOW_API_EGL`, 3.`NATIVE_WINDOW_API_CPU`, 4.`NATIVE_WINDOW_API_MEDIA`, 5.`NATIVE_WINDOW_API_CAMERA`, |Value obtained from FuzzedDataProvider| -|`dataspace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_bufferQueue_fuzzer -``` -2. To run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_bufferQueue_fuzzer/libgui_bufferQueue_fuzzer -``` - -# <a name="libgui_consumer_fuzzer"></a> Fuzzer for Libgui_Consumer - -Libgui_Consumer supports the following parameters: -1. GraphicWidth (parameter name:`graphicWidth`) -2. GraphicHeight (parameter name:`graphicHeight`) -4. TransformHint (parameter name:`outTransformHint`) -5. GraphicPixelFormat (parameter name:`format`) -6. Usage (parameter name:`usage`) - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_consumer_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_consumer_fuzzer/libgui_consumer_fuzzer -``` - -# <a name="libgui_displayEvent_fuzzer"></a> Fuzzer for LibGui_DisplayEvent - -LibGui_DisplayEvent supports the following parameters: -1. DisplayEventType (parameter name:`type`) -2. Events (parameter name:`events`) -3. VsyncSource (parameter name:`vsyncSource`) -4. EventRegistrationFlags (parameter name:`flags`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider| -|`flags`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride`, |Value obtained from FuzzedDataProvider| -|`type`| 0.`DisplayEventReceiver::DISPLAY_EVENT_NULL`, 1.`DisplayEventReceiver::DISPLAY_EVENT_VSYNC`, 2.`DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG`, 3.`DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE`, 4.`DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE`, 5.`DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH`, |Value obtained from FuzzedDataProvider| -|`events`| 0.`Looper::EVENT_INPUT`, 1.`Looper::EVENT_OUTPUT`, 2.`Looper::EVENT_ERROR`, 3.`Looper::EVENT_HANGUP`, 4.`Looper::EVENT_INVALID`, |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_displayEvent_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_displayEvent_fuzzer/libgui_displayEvent_fuzzer -``` diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp deleted file mode 100644 index 2e270b721f..0000000000 --- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2022 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 <android-base/stringprintf.h> -#include <gui/BufferQueueConsumer.h> -#include <gui/BufferQueueCore.h> -#include <gui/BufferQueueProducer.h> -#include <gui/bufferqueue/2.0/types.h> -#include <system/window.h> - -#include <libgui_fuzzer_utils.h> - -using namespace android; -using namespace hardware::graphics::bufferqueue; -using namespace V1_0::utils; -using namespace V2_0::utils; - -constexpr int32_t kMaxBytes = 256; - -constexpr int32_t kError[] = { - OK, NO_MEMORY, NO_INIT, BAD_VALUE, DEAD_OBJECT, INVALID_OPERATION, - TIMED_OUT, WOULD_BLOCK, UNKNOWN_ERROR, ALREADY_EXISTS, -}; - -constexpr int32_t kAPIConnection[] = { - BufferQueueCore::CURRENTLY_CONNECTED_API, - BufferQueueCore::NO_CONNECTED_API, - NATIVE_WINDOW_API_EGL, - NATIVE_WINDOW_API_CPU, - NATIVE_WINDOW_API_MEDIA, - NATIVE_WINDOW_API_CAMERA, -}; - -class BufferQueueFuzzer { -public: - BufferQueueFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - void invokeTypes(); - void invokeH2BGraphicBufferV1(); - void invokeH2BGraphicBufferV2(); - void invokeBufferQueueConsumer(); - void invokeBufferQueueProducer(); - void invokeBlastBufferQueue(); - void invokeQuery(sp<BufferQueueProducer>); - void invokeQuery(sp<V1_0::utils::H2BGraphicBufferProducer>); - void invokeQuery(sp<V2_0::utils::H2BGraphicBufferProducer>); - void invokeAcquireBuffer(sp<BufferQueueConsumer>); - void invokeOccupancyTracker(sp<BufferQueueConsumer>); - sp<SurfaceControl> makeSurfaceControl(); - sp<BLASTBufferQueue> makeBLASTBufferQueue(sp<SurfaceControl>); - - FuzzedDataProvider mFdp; -}; - -class ManageResourceHandle { -public: - ManageResourceHandle(FuzzedDataProvider* fdp) { - mNativeHandle = native_handle_create(0 /*numFds*/, 1 /*numInts*/); - mShouldOwn = fdp->ConsumeBool(); - mStream = NativeHandle::create(mNativeHandle, mShouldOwn); - } - ~ManageResourceHandle() { - if (!mShouldOwn) { - native_handle_close(mNativeHandle); - native_handle_delete(mNativeHandle); - } - } - sp<NativeHandle> getStream() { return mStream; } - -private: - bool mShouldOwn; - sp<NativeHandle> mStream; - native_handle_t* mNativeHandle; -}; - -sp<SurfaceControl> BufferQueueFuzzer::makeSurfaceControl() { - sp<IBinder> handle; - const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient()); - sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient); - sp<BnGraphicBufferProducer> producer; - uint32_t layerId = mFdp.ConsumeIntegral<uint32_t>(); - std::string layerName = base::StringPrintf("#%d", layerId); - return sp<SurfaceControl>::make(client, handle, layerId, layerName, - mFdp.ConsumeIntegral<int32_t>(), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>(), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); -} - -sp<BLASTBufferQueue> BufferQueueFuzzer::makeBLASTBufferQueue(sp<SurfaceControl> surface) { - return sp<BLASTBufferQueue>::make(mFdp.ConsumeRandomLengthString(kMaxBytes), surface, - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>()); -} - -void BufferQueueFuzzer::invokeBlastBufferQueue() { - sp<SurfaceControl> surface = makeSurfaceControl(); - sp<BLASTBufferQueue> queue = makeBLASTBufferQueue(surface); - - BufferItem item; - queue->onFrameAvailable(item); - queue->onFrameReplaced(item); - uint64_t bufferId = mFdp.ConsumeIntegral<uint64_t>(); - queue->onFrameDequeued(bufferId); - queue->onFrameCancelled(bufferId); - - SurfaceComposerClient::Transaction next; - uint64_t frameNumber = mFdp.ConsumeIntegral<uint64_t>(); - queue->mergeWithNextTransaction(&next, frameNumber); - queue->applyPendingTransactions(frameNumber); - - queue->update(surface, mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>()); - queue->setFrameRate(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeIntegral<int8_t>(), - mFdp.ConsumeBool() /*shouldBeSeamless*/); - FrameTimelineInfo info; - queue->setFrameTimelineInfo(mFdp.ConsumeIntegral<uint64_t>(), info); - - ManageResourceHandle handle(&mFdp); - queue->setSidebandStream(handle.getStream()); - - queue->getLastTransformHint(); - queue->getLastAcquiredFrameNum(); - - CompositorTiming compTiming; - sp<Fence> previousFence = new Fence(memfd_create("pfd", MFD_ALLOW_SEALING)); - sp<Fence> gpuFence = new Fence(memfd_create("gfd", MFD_ALLOW_SEALING)); - FrameEventHistoryStats frameStats(frameNumber, mFdp.ConsumeIntegral<uint64_t>(), gpuFence, - compTiming, mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>()); - std::vector<SurfaceControlStats> stats; - sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); - SurfaceControlStats controlStats(surface, mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>(), presentFence, previousFence, - mFdp.ConsumeIntegral<uint32_t>(), frameStats, - mFdp.ConsumeIntegral<uint32_t>()); - stats.push_back(controlStats); -} - -void BufferQueueFuzzer::invokeQuery(sp<BufferQueueProducer> producer) { - int32_t value; - producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); -} - -void BufferQueueFuzzer::invokeQuery(sp<V1_0::utils::H2BGraphicBufferProducer> producer) { - int32_t value; - producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); -} - -void BufferQueueFuzzer::invokeQuery(sp<V2_0::utils::H2BGraphicBufferProducer> producer) { - int32_t value; - producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); -} - -void BufferQueueFuzzer::invokeBufferQueueProducer() { - sp<BufferQueueCore> core(new BufferQueueCore()); - sp<BufferQueueProducer> producer(new BufferQueueProducer(core)); - const sp<android::IProducerListener> listener; - android::IGraphicBufferProducer::QueueBufferOutput output; - uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); - producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); - - sp<GraphicBuffer> buffer; - int32_t slot = mFdp.ConsumeIntegral<int32_t>(); - uint32_t maxBuffers = mFdp.ConsumeIntegral<uint32_t>(); - producer->requestBuffer(slot, &buffer); - producer->setMaxDequeuedBufferCount(maxBuffers); - producer->setAsyncMode(mFdp.ConsumeBool() /*async*/); - - android::IGraphicBufferProducer::QueueBufferInput input; - producer->attachBuffer(&slot, buffer); - producer->queueBuffer(slot, input, &output); - - int32_t format = mFdp.ConsumeIntegral<int32_t>(); - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); - uint64_t outBufferAge; - FrameEventHistoryDelta outTimestamps; - sp<android::Fence> fence; - producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, - &outTimestamps); - producer->detachBuffer(slot); - producer->detachNextBuffer(&buffer, &fence); - producer->cancelBuffer(slot, fence); - - invokeQuery(producer); - - ManageResourceHandle handle(&mFdp); - producer->setSidebandStream(handle.getStream()); - - producer->allocateBuffers(width, height, format, usage); - producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); - producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); - producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); - producer->setLegacyBufferDrop(mFdp.ConsumeBool() /*drop*/); - producer->setAutoPrerotation(mFdp.ConsumeBool() /*autoPrerotation*/); - - producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); - producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); - producer->disconnect(api); -} - -void BufferQueueFuzzer::invokeAcquireBuffer(sp<BufferQueueConsumer> consumer) { - BufferItem item; - consumer->acquireBuffer(&item, mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint64_t>()); -} - -void BufferQueueFuzzer::invokeOccupancyTracker(sp<BufferQueueConsumer> consumer) { - String8 outResult; - String8 prefix((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); - consumer->dumpState(prefix, &outResult); - - std::vector<OccupancyTracker::Segment> outHistory; - consumer->getOccupancyHistory(mFdp.ConsumeBool() /*forceFlush*/, &outHistory); -} - -void BufferQueueFuzzer::invokeBufferQueueConsumer() { - sp<BufferQueueCore> core(new BufferQueueCore()); - sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core)); - sp<android::IConsumerListener> listener; - consumer->consumerConnect(listener, mFdp.ConsumeBool() /*controlledByApp*/); - invokeAcquireBuffer(consumer); - - int32_t slot = mFdp.ConsumeIntegral<int32_t>(); - sp<GraphicBuffer> buffer = - new GraphicBuffer(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint64_t>()); - consumer->attachBuffer(&slot, buffer); - consumer->detachBuffer(slot); - - consumer->setDefaultBufferSize(mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); - consumer->setMaxBufferCount(mFdp.ConsumeIntegral<int32_t>()); - consumer->setMaxAcquiredBufferCount(mFdp.ConsumeIntegral<int32_t>()); - - String8 name((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); - consumer->setConsumerName(name); - consumer->setDefaultBufferFormat(mFdp.ConsumeIntegral<int32_t>()); - android_dataspace dataspace = - static_cast<android_dataspace>(mFdp.PickValueInArray(kDataspaces)); - consumer->setDefaultBufferDataSpace(dataspace); - - consumer->setTransformHint(mFdp.ConsumeIntegral<uint32_t>()); - consumer->setConsumerUsageBits(mFdp.ConsumeIntegral<uint64_t>()); - consumer->setConsumerIsProtected(mFdp.ConsumeBool() /*isProtected*/); - invokeOccupancyTracker(consumer); - - sp<Fence> releaseFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); - consumer->releaseBuffer(mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint64_t>(), - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); - consumer->consumerDisconnect(); -} - -void BufferQueueFuzzer::invokeTypes() { - HStatus hStatus; - int32_t status = mFdp.PickValueInArray(kError); - bool bufferNeedsReallocation = mFdp.ConsumeBool(); - bool releaseAllBuffers = mFdp.ConsumeBool(); - b2h(status, &hStatus, &bufferNeedsReallocation, &releaseAllBuffers); - h2b(hStatus, &status); - - HConnectionType type; - int32_t apiConnection = mFdp.PickValueInArray(kAPIConnection); - b2h(apiConnection, &type); - h2b(type, &apiConnection); -} - -void BufferQueueFuzzer::invokeH2BGraphicBufferV1() { - sp<V1_0::utils::H2BGraphicBufferProducer> producer( - new V1_0::utils::H2BGraphicBufferProducer(new FakeGraphicBufferProducerV1())); - const sp<android::IProducerListener> listener; - android::IGraphicBufferProducer::QueueBufferOutput output; - uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); - producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); - - sp<GraphicBuffer> buffer; - int32_t slot = mFdp.ConsumeIntegral<int32_t>(); - producer->requestBuffer(slot, &buffer); - producer->setMaxDequeuedBufferCount(mFdp.ConsumeIntegral<int32_t>()); - producer->setAsyncMode(mFdp.ConsumeBool()); - - android::IGraphicBufferProducer::QueueBufferInput input; - input.fence = new Fence(memfd_create("ffd", MFD_ALLOW_SEALING)); - producer->attachBuffer(&slot, buffer); - producer->queueBuffer(slot, input, &output); - - int32_t format = mFdp.ConsumeIntegral<int32_t>(); - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); - uint64_t outBufferAge; - FrameEventHistoryDelta outTimestamps; - sp<android::Fence> fence; - producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, - &outTimestamps); - producer->detachBuffer(slot); - producer->cancelBuffer(slot, fence); - - invokeQuery(producer); - - ManageResourceHandle handle(&mFdp); - producer->setSidebandStream(handle.getStream()); - - producer->allocateBuffers(width, height, format, usage); - producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); - producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); - producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); - - producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); - producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); - producer->disconnect(api); -} - -void BufferQueueFuzzer::invokeH2BGraphicBufferV2() { - sp<V2_0::utils::H2BGraphicBufferProducer> producer( - new V2_0::utils::H2BGraphicBufferProducer(new FakeGraphicBufferProducerV2())); - const sp<android::IProducerListener> listener; - android::IGraphicBufferProducer::QueueBufferOutput output; - uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); - producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); - - sp<GraphicBuffer> buffer; - int32_t slot = mFdp.ConsumeIntegral<int32_t>(); - producer->requestBuffer(slot, &buffer); - producer->setMaxDequeuedBufferCount(mFdp.ConsumeIntegral<uint32_t>()); - producer->setAsyncMode(mFdp.ConsumeBool()); - - android::IGraphicBufferProducer::QueueBufferInput input; - input.fence = new Fence(memfd_create("ffd", MFD_ALLOW_SEALING)); - producer->attachBuffer(&slot, buffer); - producer->queueBuffer(slot, input, &output); - - int32_t format = mFdp.ConsumeIntegral<int32_t>(); - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); - uint64_t outBufferAge; - FrameEventHistoryDelta outTimestamps; - sp<android::Fence> fence; - producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, - &outTimestamps); - producer->detachBuffer(slot); - producer->cancelBuffer(slot, fence); - - invokeQuery(producer); - - ManageResourceHandle handle(&mFdp); - producer->setSidebandStream(handle.getStream()); - - producer->allocateBuffers(width, height, format, usage); - producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); - producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); - producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); - - producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); - producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); - producer->disconnect(api); -} - -void BufferQueueFuzzer::process() { - invokeBlastBufferQueue(); - invokeH2BGraphicBufferV1(); - invokeH2BGraphicBufferV2(); - invokeTypes(); - invokeBufferQueueConsumer(); - invokeBufferQueueProducer(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - BufferQueueFuzzer bufferQueueFuzzer(data, size); - bufferQueueFuzzer.process(); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp b/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp deleted file mode 100644 index 24a046d3a9..0000000000 --- a/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2022 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 <gui/BufferQueueConsumer.h> -#include <gui/BufferQueueCore.h> -#include <gui/BufferQueueProducer.h> -#include <gui/GLConsumer.h> -#include <libgui_fuzzer_utils.h> - -using namespace android; - -constexpr int32_t kMinBuffer = 0; -constexpr int32_t kMaxBuffer = 100000; - -class ConsumerFuzzer { -public: - ConsumerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - FuzzedDataProvider mFdp; -}; - -void ConsumerFuzzer::process() { - sp<BufferQueueCore> core(new BufferQueueCore()); - sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); - - uint64_t maxBuffers = mFdp.ConsumeIntegralInRange<uint64_t>(kMinBuffer, kMaxBuffer); - sp<CpuConsumer> cpu( - new CpuConsumer(consumer, maxBuffers, mFdp.ConsumeBool() /*controlledByApp*/)); - CpuConsumer::LockedBuffer lockBuffer; - cpu->lockNextBuffer(&lockBuffer); - cpu->unlockBuffer(lockBuffer); - cpu->abandon(); - - uint32_t tex = mFdp.ConsumeIntegral<uint32_t>(); - sp<GLConsumer> glComsumer(new GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, - mFdp.ConsumeBool() /*useFenceSync*/, - mFdp.ConsumeBool() /*isControlledByApp*/)); - sp<Fence> releaseFence = new Fence(memfd_create("rfd", MFD_ALLOW_SEALING)); - glComsumer->setReleaseFence(releaseFence); - glComsumer->updateTexImage(); - glComsumer->releaseTexImage(); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint64_t>()); - float mtx[16]; - glComsumer->getTransformMatrix(mtx); - glComsumer->computeTransformMatrix(mtx, buffer, getRect(&mFdp), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeBool() /*filtering*/); - glComsumer->scaleDownCrop(getRect(&mFdp), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); - - glComsumer->setDefaultBufferSize(mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); - glComsumer->setFilteringEnabled(mFdp.ConsumeBool() /*enabled*/); - - glComsumer->setConsumerUsageBits(mFdp.ConsumeIntegral<uint64_t>()); - glComsumer->attachToContext(tex); - glComsumer->abandon(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - ConsumerFuzzer consumerFuzzer(data, size); - consumerFuzzer.process(); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp deleted file mode 100644 index 0d2a52b576..0000000000 --- a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2022 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 <android/gui/ISurfaceComposer.h> - -#include <libgui_fuzzer_utils.h> - -using namespace android; - -constexpr gui::ISurfaceComposer::VsyncSource kVsyncSource[] = { - gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, - gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger, -}; - -constexpr gui::ISurfaceComposer::EventRegistration kEventRegistration[] = { - gui::ISurfaceComposer::EventRegistration::modeChanged, - gui::ISurfaceComposer::EventRegistration::frameRateOverride, -}; - -constexpr uint32_t kDisplayEvent[] = { - DisplayEventReceiver::DISPLAY_EVENT_NULL, - DisplayEventReceiver::DISPLAY_EVENT_VSYNC, - DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, - DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, - DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, - DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, -}; - -constexpr int32_t kEvents[] = { - Looper::EVENT_INPUT, Looper::EVENT_OUTPUT, Looper::EVENT_ERROR, - Looper::EVENT_HANGUP, Looper::EVENT_INVALID, -}; - -DisplayEventReceiver::Event buildDisplayEvent(FuzzedDataProvider* fdp, uint32_t type, - DisplayEventReceiver::Event event) { - switch (type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: { - event.vsync.count = fdp->ConsumeIntegral<uint32_t>(); - event.vsync.vsyncData.frameInterval = fdp->ConsumeIntegral<uint64_t>(); - event.vsync.vsyncData.preferredFrameTimelineIndex = fdp->ConsumeIntegral<uint32_t>(); - for (size_t idx = 0; idx < gui::VsyncEventData::kFrameTimelinesCapacity; ++idx) { - event.vsync.vsyncData.frameTimelines[idx].vsyncId = fdp->ConsumeIntegral<int64_t>(); - event.vsync.vsyncData.frameTimelines[idx].deadlineTimestamp = - fdp->ConsumeIntegral<uint64_t>(); - event.vsync.vsyncData.frameTimelines[idx].expectedPresentationTime = - fdp->ConsumeIntegral<uint64_t>(); - } - break; - - } - case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: { - event.hotplug = - DisplayEventReceiver::Event::Hotplug{fdp->ConsumeBool() /*connected*/, - fdp->ConsumeIntegral< - int32_t>() /*connectionError*/}; - break; - } - case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: { - event.modeChange = - DisplayEventReceiver::Event::ModeChange{fdp->ConsumeIntegral<int32_t>(), - fdp->ConsumeIntegral<int64_t>()}; - break; - } - case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE: - case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: { - event.frameRateOverride = - DisplayEventReceiver::Event::FrameRateOverride{fdp->ConsumeIntegral<uint32_t>(), - fdp->ConsumeFloatingPoint< - float>()}; - break; - } - } - return event; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - FuzzedDataProvider fdp(data, size); - sp<Looper> looper; - sp<FakeDisplayEventDispatcher> dispatcher( - new FakeDisplayEventDispatcher(looper, fdp.PickValueInArray(kVsyncSource), - fdp.PickValueInArray(kEventRegistration))); - - dispatcher->initialize(); - DisplayEventReceiver::Event event; - uint32_t type = fdp.PickValueInArray(kDisplayEvent); - PhysicalDisplayId displayId; - event.header = - DisplayEventReceiver::Event::Header{type, displayId, fdp.ConsumeIntegral<int64_t>()}; - event = buildDisplayEvent(&fdp, type, event); - - dispatcher->injectEvent(event); - dispatcher->handleEvent(0, fdp.PickValueInArray(kEvents), nullptr); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h deleted file mode 100644 index 9933680c4b..0000000000 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 2021 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 <android/gui/BnRegionSamplingListener.h> -#include <android/gui/BnSurfaceComposer.h> -#include <android/gui/BnSurfaceComposerClient.h> -#include <android/gui/IDisplayEventConnection.h> -#include <android/gui/ISurfaceComposerClient.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <gmock/gmock.h> -#include <gui/BLASTBufferQueue.h> -#include <gui/DisplayEventDispatcher.h> -#include <gui/IGraphicBufferProducer.h> -#include <gui/LayerDebugInfo.h> -#include <gui/LayerState.h> -#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> -#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> -#include <ui/fuzzer/FuzzableDataspaces.h> - -namespace android { - -constexpr uint32_t kOrientation[] = { - ui::Transform::ROT_0, ui::Transform::FLIP_H, ui::Transform::FLIP_V, - ui::Transform::ROT_90, ui::Transform::ROT_180, ui::Transform::ROT_270, -}; - -Rect getRect(FuzzedDataProvider* fdp) { - const int32_t left = fdp->ConsumeIntegral<int32_t>(); - const int32_t top = fdp->ConsumeIntegral<int32_t>(); - const int32_t right = fdp->ConsumeIntegral<int32_t>(); - const int32_t bottom = fdp->ConsumeIntegral<int32_t>(); - return Rect(left, top, right, bottom); -} - -gui::DisplayBrightness getBrightness(FuzzedDataProvider* fdp) { - static constexpr float kMinBrightness = 0; - static constexpr float kMaxBrightness = 1; - gui::DisplayBrightness brightness; - brightness.sdrWhitePoint = - fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); - brightness.sdrWhitePointNits = - fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); - brightness.displayBrightness = - fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); - brightness.displayBrightnessNits = - fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); - return brightness; -} - -class FakeBnSurfaceComposer : public gui::BnSurfaceComposer { -public: - MOCK_METHOD(binder::Status, bootFinished, (), (override)); - MOCK_METHOD(binder::Status, createDisplayEventConnection, - (gui::ISurfaceComposer::VsyncSource, gui::ISurfaceComposer::EventRegistration, - const sp<IBinder>& /*layerHandle*/, sp<gui::IDisplayEventConnection>*), - (override)); - MOCK_METHOD(binder::Status, createConnection, (sp<gui::ISurfaceComposerClient>*), (override)); - MOCK_METHOD(binder::Status, createDisplay, (const std::string&, bool, float, sp<IBinder>*), - (override)); - MOCK_METHOD(binder::Status, destroyDisplay, (const sp<IBinder>&), (override)); - MOCK_METHOD(binder::Status, getPhysicalDisplayIds, (std::vector<int64_t>*), (override)); - MOCK_METHOD(binder::Status, getPhysicalDisplayToken, (int64_t, sp<IBinder>*), (override)); - MOCK_METHOD(binder::Status, setPowerMode, (const sp<IBinder>&, int), (override)); - MOCK_METHOD(binder::Status, getSupportedFrameTimestamps, (std::vector<FrameEvent>*), - (override)); - MOCK_METHOD(binder::Status, getDisplayStats, (const sp<IBinder>&, gui::DisplayStatInfo*), - (override)); - MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*), - (override)); - MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*), - (override)); - MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*), - (override)); - MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken, - (const sp<IBinder>&, gui::DynamicDisplayInfo*), (override)); - MOCK_METHOD(binder::Status, getDisplayNativePrimaries, - (const sp<IBinder>&, gui::DisplayPrimaries*), (override)); - MOCK_METHOD(binder::Status, setActiveColorMode, (const sp<IBinder>&, int), (override)); - MOCK_METHOD(binder::Status, setBootDisplayMode, (const sp<IBinder>&, int), (override)); - MOCK_METHOD(binder::Status, clearBootDisplayMode, (const sp<IBinder>&), (override)); - MOCK_METHOD(binder::Status, getBootDisplayModeSupport, (bool*), (override)); - MOCK_METHOD(binder::Status, getHdrConversionCapabilities, - (std::vector<gui::HdrConversionCapability>*), (override)); - MOCK_METHOD(binder::Status, setHdrConversionStrategy, - (const gui::HdrConversionStrategy&, int32_t*), (override)); - MOCK_METHOD(binder::Status, getHdrOutputConversionSupport, (bool*), (override)); - MOCK_METHOD(binder::Status, setAutoLowLatencyMode, (const sp<IBinder>&, bool), (override)); - MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override)); - MOCK_METHOD(binder::Status, captureDisplay, - (const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); - MOCK_METHOD(binder::Status, captureDisplayById, - (int64_t, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&), (override)); - MOCK_METHOD(binder::Status, captureLayers, - (const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); - MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override)); - MOCK_METHOD(binder::Status, getAnimationFrameStats, (gui::FrameStats*), (override)); - MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&), - (override)); - MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override)); - MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override)); - MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*), - (override)); - MOCK_METHOD(binder::Status, getDisplayedContentSamplingAttributes, - (const sp<IBinder>&, gui::ContentSamplingAttributes*), (override)); - MOCK_METHOD(binder::Status, setDisplayContentSamplingEnabled, - (const sp<IBinder>&, bool, int8_t, int64_t), (override)); - MOCK_METHOD(binder::Status, getDisplayedContentSample, - (const sp<IBinder>&, int64_t, int64_t, gui::DisplayedFrameStats*), (override)); - MOCK_METHOD(binder::Status, getProtectedContentSupport, (bool*), (override)); - MOCK_METHOD(binder::Status, isWideColorDisplay, (const sp<IBinder>&, bool*), (override)); - MOCK_METHOD(binder::Status, addRegionSamplingListener, - (const gui::ARect&, const sp<IBinder>&, const sp<gui::IRegionSamplingListener>&), - (override)); - MOCK_METHOD(binder::Status, removeRegionSamplingListener, - (const sp<gui::IRegionSamplingListener>&), (override)); - MOCK_METHOD(binder::Status, addFpsListener, (int32_t, const sp<gui::IFpsListener>&), - (override)); - MOCK_METHOD(binder::Status, removeFpsListener, (const sp<gui::IFpsListener>&), (override)); - MOCK_METHOD(binder::Status, addTunnelModeEnabledListener, - (const sp<gui::ITunnelModeEnabledListener>&), (override)); - MOCK_METHOD(binder::Status, removeTunnelModeEnabledListener, - (const sp<gui::ITunnelModeEnabledListener>&), (override)); - MOCK_METHOD(binder::Status, setDesiredDisplayModeSpecs, - (const sp<IBinder>&, const gui::DisplayModeSpecs&), (override)); - MOCK_METHOD(binder::Status, getDesiredDisplayModeSpecs, - (const sp<IBinder>&, gui::DisplayModeSpecs*), (override)); - MOCK_METHOD(binder::Status, getDisplayBrightnessSupport, (const sp<IBinder>&, bool*), - (override)); - MOCK_METHOD(binder::Status, setDisplayBrightness, - (const sp<IBinder>&, const gui::DisplayBrightness&), (override)); - MOCK_METHOD(binder::Status, addHdrLayerInfoListener, - (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override)); - MOCK_METHOD(binder::Status, removeHdrLayerInfoListener, - (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override)); - MOCK_METHOD(binder::Status, notifyPowerBoost, (int), (override)); - MOCK_METHOD(binder::Status, setGlobalShadowSettings, - (const gui::Color&, const gui::Color&, float, float, float), (override)); - MOCK_METHOD(binder::Status, getDisplayDecorationSupport, - (const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override)); - MOCK_METHOD(binder::Status, setGameModeFrameRateOverride, (int32_t, float), (override)); - MOCK_METHOD(binder::Status, setGameDefaultFrameRateOverride, (int32_t, float), (override)); - MOCK_METHOD(binder::Status, enableRefreshRateOverlay, (bool), (override)); - MOCK_METHOD(binder::Status, setDebugFlash, (int), (override)); - MOCK_METHOD(binder::Status, scheduleComposite, (), (override)); - MOCK_METHOD(binder::Status, scheduleCommit, (), (override)); - MOCK_METHOD(binder::Status, forceClientComposition, (bool), (override)); - MOCK_METHOD(binder::Status, updateSmallAreaDetection, - (const std::vector<int32_t>&, const std::vector<float>&), (override)); - MOCK_METHOD(binder::Status, setSmallAreaDetectionThreshold, (int32_t, float), (override)); - MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override)); - MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override)); - MOCK_METHOD(binder::Status, addWindowInfosListener, - (const sp<gui::IWindowInfosListener>&, gui::WindowInfosListenerInfo*), (override)); - MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&), - (override)); - MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override)); - MOCK_METHOD(binder::Status, getStalledTransactionInfo, - (int32_t, std::optional<gui::StalledTransactionInfo>*), (override)); - MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override)); -}; - -class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient { -public: - MOCK_METHOD(binder::Status, createSurface, - (const std::string& name, int32_t flags, const sp<IBinder>& parent, - const gui::LayerMetadata& metadata, gui::CreateSurfaceResult* outResult), - (override)); - - MOCK_METHOD(binder::Status, clearLayerFrameStats, (const sp<IBinder>& handle), (override)); - - MOCK_METHOD(binder::Status, getLayerFrameStats, - (const sp<IBinder>& handle, gui::FrameStats* outStats), (override)); - - MOCK_METHOD(binder::Status, mirrorSurface, - (const sp<IBinder>& mirrorFromHandle, gui::CreateSurfaceResult* outResult), - (override)); - - MOCK_METHOD(binder::Status, mirrorDisplay, - (int64_t displayId, gui::CreateSurfaceResult* outResult), (override)); - - MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override)); -}; - -class FakeDisplayEventDispatcher : public DisplayEventDispatcher { -public: - FakeDisplayEventDispatcher(const sp<Looper>& looper, - gui::ISurfaceComposer::VsyncSource vsyncSource, - gui::ISurfaceComposer::EventRegistration eventRegistration) - : DisplayEventDispatcher(looper, vsyncSource, eventRegistration){}; - - MOCK_METHOD4(dispatchVsync, void(nsecs_t, PhysicalDisplayId, uint32_t, VsyncEventData)); - MOCK_METHOD3(dispatchHotplug, void(nsecs_t, PhysicalDisplayId, bool)); - MOCK_METHOD2(dispatchHotplugConnectionError, void(nsecs_t, int32_t)); - MOCK_METHOD4(dispatchModeChanged, void(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t)); - MOCK_METHOD2(dispatchNullEvent, void(nsecs_t, PhysicalDisplayId)); - MOCK_METHOD3(dispatchFrameRateOverrides, - void(nsecs_t, PhysicalDisplayId, std::vector<FrameRateOverride>)); -}; - -} // namespace android - -namespace android::hardware { - -namespace graphics::bufferqueue::V1_0::utils { - -class FakeGraphicBufferProducerV1 : public HGraphicBufferProducer { -public: - FakeGraphicBufferProducerV1() { - ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return 0; }); - ON_CALL(*this, setAsyncMode).WillByDefault([]() { return 0; }); - ON_CALL(*this, detachBuffer).WillByDefault([]() { return 0; }); - ON_CALL(*this, cancelBuffer).WillByDefault([]() { return 0; }); - ON_CALL(*this, disconnect).WillByDefault([]() { return 0; }); - ON_CALL(*this, setSidebandStream).WillByDefault([]() { return 0; }); - ON_CALL(*this, allowAllocation).WillByDefault([]() { return 0; }); - ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return 0; }); - ON_CALL(*this, setSharedBufferMode).WillByDefault([]() { return 0; }); - ON_CALL(*this, setAutoRefresh).WillByDefault([]() { return 0; }); - ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return 0; }); - ON_CALL(*this, setLegacyBufferDrop).WillByDefault([]() { return 0; }); - }; - MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb)); - MOCK_METHOD1(setMaxDequeuedBufferCount, Return<int32_t>(int32_t)); - MOCK_METHOD1(setAsyncMode, Return<int32_t>(bool)); - MOCK_METHOD6(dequeueBuffer, - Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t, - bool, dequeueBuffer_cb)); - MOCK_METHOD1(detachBuffer, Return<int32_t>(int)); - MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb)); - MOCK_METHOD2(attachBuffer, Return<void>(const media::V1_0::AnwBuffer&, attachBuffer_cb)); - MOCK_METHOD3( - queueBuffer, - Return<void>( - int, - const graphics::bufferqueue::V1_0::IGraphicBufferProducer::QueueBufferInput&, - queueBuffer_cb)); - MOCK_METHOD2(cancelBuffer, Return<int32_t>(int, const hidl_handle&)); - MOCK_METHOD2(query, Return<void>(int32_t, query_cb)); - MOCK_METHOD4(connect, - Return<void>(const sp<graphics::bufferqueue::V1_0::IProducerListener>&, int32_t, - bool, connect_cb)); - MOCK_METHOD2(disconnect, - Return<int32_t>( - int, graphics::bufferqueue::V1_0::IGraphicBufferProducer::DisconnectMode)); - MOCK_METHOD1(setSidebandStream, Return<int32_t>(const hidl_handle&)); - MOCK_METHOD4(allocateBuffers, - Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t)); - MOCK_METHOD1(allowAllocation, Return<int32_t>(bool)); - MOCK_METHOD1(setGenerationNumber, Return<int32_t>(uint32_t)); - MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb)); - MOCK_METHOD1(setSharedBufferMode, Return<int32_t>(bool)); - MOCK_METHOD1(setAutoRefresh, Return<int32_t>(bool)); - MOCK_METHOD1(setDequeueTimeout, Return<int32_t>(nsecs_t)); - MOCK_METHOD1(setLegacyBufferDrop, Return<int32_t>(bool)); - MOCK_METHOD1(getLastQueuedBuffer, Return<void>(getLastQueuedBuffer_cb)); - MOCK_METHOD1(getFrameTimestamps, Return<void>(getFrameTimestamps_cb)); - MOCK_METHOD1(getUniqueId, Return<void>(getUniqueId_cb)); -}; - -}; // namespace graphics::bufferqueue::V1_0::utils - -namespace graphics::bufferqueue::V2_0::utils { - -class FakeGraphicBufferProducerV2 : public HGraphicBufferProducer { -public: - FakeGraphicBufferProducerV2() { - ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, setAsyncMode).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, detachBuffer).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, cancelBuffer).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, disconnect).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, allocateBuffers).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, allowAllocation).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, getUniqueId).WillByDefault([]() { return 0; }); - }; - MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb)); - MOCK_METHOD1(setMaxDequeuedBufferCount, Return<graphics::bufferqueue::V2_0::Status>(int)); - MOCK_METHOD1(setAsyncMode, Return<graphics::bufferqueue::V2_0::Status>(bool)); - MOCK_METHOD2( - dequeueBuffer, - Return<void>( - const graphics::bufferqueue::V2_0::IGraphicBufferProducer::DequeueBufferInput&, - dequeueBuffer_cb)); - MOCK_METHOD1(detachBuffer, Return<graphics::bufferqueue::V2_0::Status>(int)); - MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb)); - MOCK_METHOD3(attachBuffer, - Return<void>(const graphics::common::V1_2::HardwareBuffer&, uint32_t, - attachBuffer_cb)); - MOCK_METHOD3( - queueBuffer, - Return<void>( - int, - const graphics::bufferqueue::V2_0::IGraphicBufferProducer::QueueBufferInput&, - queueBuffer_cb)); - MOCK_METHOD2(cancelBuffer, - Return<graphics::bufferqueue::V2_0::Status>(int, const hidl_handle&)); - MOCK_METHOD2(query, Return<void>(int32_t, query_cb)); - MOCK_METHOD4(connect, - Return<void>(const sp<graphics::bufferqueue::V2_0::IProducerListener>&, - graphics::bufferqueue::V2_0::ConnectionType, bool, connect_cb)); - MOCK_METHOD1(disconnect, - Return<graphics::bufferqueue::V2_0::Status>( - graphics::bufferqueue::V2_0::ConnectionType)); - MOCK_METHOD4(allocateBuffers, - Return<graphics::bufferqueue::V2_0::Status>(uint32_t, uint32_t, uint32_t, - uint64_t)); - MOCK_METHOD1(allowAllocation, Return<graphics::bufferqueue::V2_0::Status>(bool)); - MOCK_METHOD1(setGenerationNumber, Return<graphics::bufferqueue::V2_0::Status>(uint32_t)); - MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb)); - MOCK_METHOD1(setDequeueTimeout, Return<graphics::bufferqueue::V2_0::Status>(int64_t)); - MOCK_METHOD0(getUniqueId, Return<uint64_t>()); -}; - -}; // namespace graphics::bufferqueue::V2_0::utils -}; // namespace android::hardware diff --git a/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp b/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp deleted file mode 100644 index 9f0f6cac19..0000000000 --- a/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2022 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 <gui/BufferQueueConsumer.h> -#include <gui/BufferQueueCore.h> -#include <gui/BufferQueueProducer.h> -#include <gui/LayerMetadata.h> -#include <gui/OccupancyTracker.h> -#include <gui/StreamSplitter.h> -#include <gui/Surface.h> -#include <gui/SurfaceControl.h> -#include <gui/view/Surface.h> -#include <libgui_fuzzer_utils.h> -#include "android/view/LayerMetadataKey.h" - -using namespace android; - -constexpr int32_t kMaxBytes = 256; -constexpr int32_t kMatrixSize = 4; -constexpr int32_t kLayerMetadataKeyCount = 8; - -constexpr uint32_t kMetadataKey[] = { - (uint32_t)view::LayerMetadataKey::METADATA_OWNER_UID, - (uint32_t)view::LayerMetadataKey::METADATA_WINDOW_TYPE, - (uint32_t)view::LayerMetadataKey::METADATA_TASK_ID, - (uint32_t)view::LayerMetadataKey::METADATA_MOUSE_CURSOR, - (uint32_t)view::LayerMetadataKey::METADATA_ACCESSIBILITY_ID, - (uint32_t)view::LayerMetadataKey::METADATA_OWNER_PID, - (uint32_t)view::LayerMetadataKey::METADATA_DEQUEUE_TIME, - (uint32_t)view::LayerMetadataKey::METADATA_GAME_MODE, -}; - -class ParcelableFuzzer { -public: - ParcelableFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - void invokeStreamSplitter(); - void invokeOccupancyTracker(); - void invokeLayerDebugInfo(); - void invokeLayerMetadata(); - void invokeViewSurface(); - - FuzzedDataProvider mFdp; -}; - -void ParcelableFuzzer::invokeViewSurface() { - view::Surface surface; - surface.name = String16((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); - Parcel parcel; - surface.writeToParcel(&parcel); - parcel.setDataPosition(0); - surface.readFromParcel(&parcel); - bool nameAlreadyWritten = mFdp.ConsumeBool(); - surface.writeToParcel(&parcel, nameAlreadyWritten); - parcel.setDataPosition(0); - surface.readFromParcel(&parcel, mFdp.ConsumeBool()); -} - -void ParcelableFuzzer::invokeLayerMetadata() { - std::unordered_map<uint32_t, std::vector<uint8_t>> map; - for (size_t idx = 0; idx < kLayerMetadataKeyCount; ++idx) { - std::vector<uint8_t> data; - for (size_t idx1 = 0; idx1 < mFdp.ConsumeIntegral<uint32_t>(); ++idx1) { - data.push_back(mFdp.ConsumeIntegral<uint8_t>()); - } - map[kMetadataKey[idx]] = data; - } - LayerMetadata metadata(map); - uint32_t key = mFdp.PickValueInArray(kMetadataKey); - metadata.setInt32(key, mFdp.ConsumeIntegral<int32_t>()); - metadata.itemToString(key, (mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); - - Parcel parcel; - metadata.writeToParcel(&parcel); - parcel.setDataPosition(0); - metadata.readFromParcel(&parcel); -} - -void ParcelableFuzzer::invokeLayerDebugInfo() { - gui::LayerDebugInfo info; - info.mName = mFdp.ConsumeRandomLengthString(kMaxBytes); - info.mParentName = mFdp.ConsumeRandomLengthString(kMaxBytes); - info.mType = mFdp.ConsumeRandomLengthString(kMaxBytes); - info.mLayerStack = mFdp.ConsumeIntegral<uint32_t>(); - info.mX = mFdp.ConsumeFloatingPoint<float>(); - info.mY = mFdp.ConsumeFloatingPoint<float>(); - info.mZ = mFdp.ConsumeIntegral<uint32_t>(); - info.mWidth = mFdp.ConsumeIntegral<int32_t>(); - info.mHeight = mFdp.ConsumeIntegral<int32_t>(); - info.mActiveBufferWidth = mFdp.ConsumeIntegral<int32_t>(); - info.mActiveBufferHeight = mFdp.ConsumeIntegral<int32_t>(); - info.mActiveBufferStride = mFdp.ConsumeIntegral<int32_t>(); - info.mActiveBufferFormat = mFdp.ConsumeIntegral<int32_t>(); - info.mNumQueuedFrames = mFdp.ConsumeIntegral<int32_t>(); - - info.mFlags = mFdp.ConsumeIntegral<uint32_t>(); - info.mPixelFormat = mFdp.ConsumeIntegral<int32_t>(); - info.mTransparentRegion = Region(getRect(&mFdp)); - info.mVisibleRegion = Region(getRect(&mFdp)); - info.mSurfaceDamageRegion = Region(getRect(&mFdp)); - info.mCrop = getRect(&mFdp); - info.mDataSpace = static_cast<android_dataspace>(mFdp.PickValueInArray(kDataspaces)); - info.mColor = half4(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()); - for (size_t idx = 0; idx < kMatrixSize; ++idx) { - info.mMatrix[idx / 2][idx % 2] = mFdp.ConsumeFloatingPoint<float>(); - } - info.mIsOpaque = mFdp.ConsumeBool(); - info.mContentDirty = mFdp.ConsumeBool(); - info.mStretchEffect.width = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.height = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.vectorX = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.vectorY = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.maxAmountX = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.maxAmountY = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.mappedChildBounds = - FloatRect(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()); - - Parcel parcel; - info.writeToParcel(&parcel); - parcel.setDataPosition(0); - info.readFromParcel(&parcel); -} - -void ParcelableFuzzer::invokeOccupancyTracker() { - nsecs_t totalTime = mFdp.ConsumeIntegral<uint32_t>(); - size_t numFrames = mFdp.ConsumeIntegral<size_t>(); - float occupancyAverage = mFdp.ConsumeFloatingPoint<float>(); - OccupancyTracker::Segment segment(totalTime, numFrames, occupancyAverage, - mFdp.ConsumeBool() /*usedThirdBuffer*/); - Parcel parcel; - segment.writeToParcel(&parcel); - parcel.setDataPosition(0); - segment.readFromParcel(&parcel); -} - -void ParcelableFuzzer::invokeStreamSplitter() { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<StreamSplitter> splitter; - StreamSplitter::createSplitter(consumer, &splitter); - splitter->addOutput(producer); - std::string name = mFdp.ConsumeRandomLengthString(kMaxBytes); - splitter->setName(String8(name.c_str())); -} - -void ParcelableFuzzer::process() { - invokeStreamSplitter(); - invokeOccupancyTracker(); - invokeLayerDebugInfo(); - invokeLayerMetadata(); - invokeViewSurface(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - ParcelableFuzzer libGuiFuzzer(data, size); - libGuiFuzzer.process(); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp deleted file mode 100644 index 4daa3be36f..0000000000 --- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2022 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 <aidl/android/hardware/power/Boost.h> -#include <fuzzbinder/libbinder_driver.h> -#include <gui/Surface.h> -#include <gui/SurfaceComposerClient.h> -#include <libgui_fuzzer_utils.h> -#include "android-base/stringprintf.h" - -using namespace android; - -constexpr int32_t kRandomStringMaxBytes = 256; - -constexpr ui::ColorMode kColormodes[] = {ui::ColorMode::NATIVE, - ui::ColorMode::STANDARD_BT601_625, - ui::ColorMode::STANDARD_BT601_625_UNADJUSTED, - ui::ColorMode::STANDARD_BT601_525, - ui::ColorMode::STANDARD_BT601_525_UNADJUSTED, - ui::ColorMode::STANDARD_BT709, - ui::ColorMode::DCI_P3, - ui::ColorMode::SRGB, - ui::ColorMode::ADOBE_RGB, - ui::ColorMode::DISPLAY_P3, - ui::ColorMode::BT2020, - ui::ColorMode::BT2100_PQ, - ui::ColorMode::BT2100_HLG, - ui::ColorMode::DISPLAY_BT2020}; - -constexpr aidl::android::hardware::power::Boost kBoost[] = { - aidl::android::hardware::power::Boost::INTERACTION, - aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT, - aidl::android::hardware::power::Boost::ML_ACC, - aidl::android::hardware::power::Boost::AUDIO_LAUNCH, - aidl::android::hardware::power::Boost::CAMERA_LAUNCH, - aidl::android::hardware::power::Boost::CAMERA_SHOT, -}; - -constexpr gui::TouchOcclusionMode kMode[] = { - gui::TouchOcclusionMode::BLOCK_UNTRUSTED, - gui::TouchOcclusionMode::USE_OPACITY, - gui::TouchOcclusionMode::ALLOW, -}; - -constexpr gui::WindowInfo::Flag kFlags[] = { - gui::WindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON, - gui::WindowInfo::Flag::DIM_BEHIND, - gui::WindowInfo::Flag::BLUR_BEHIND, - gui::WindowInfo::Flag::NOT_FOCUSABLE, - gui::WindowInfo::Flag::NOT_TOUCHABLE, - gui::WindowInfo::Flag::NOT_TOUCH_MODAL, - gui::WindowInfo::Flag::TOUCHABLE_WHEN_WAKING, - gui::WindowInfo::Flag::KEEP_SCREEN_ON, - gui::WindowInfo::Flag::LAYOUT_IN_SCREEN, - gui::WindowInfo::Flag::LAYOUT_NO_LIMITS, - gui::WindowInfo::Flag::FULLSCREEN, - gui::WindowInfo::Flag::FORCE_NOT_FULLSCREEN, - gui::WindowInfo::Flag::DITHER, - gui::WindowInfo::Flag::SECURE, - gui::WindowInfo::Flag::SCALED, - gui::WindowInfo::Flag::IGNORE_CHEEK_PRESSES, - gui::WindowInfo::Flag::LAYOUT_INSET_DECOR, - gui::WindowInfo::Flag::ALT_FOCUSABLE_IM, - gui::WindowInfo::Flag::WATCH_OUTSIDE_TOUCH, - gui::WindowInfo::Flag::SHOW_WHEN_LOCKED, - gui::WindowInfo::Flag::SHOW_WALLPAPER, - gui::WindowInfo::Flag::TURN_SCREEN_ON, - gui::WindowInfo::Flag::DISMISS_KEYGUARD, - gui::WindowInfo::Flag::SPLIT_TOUCH, - gui::WindowInfo::Flag::HARDWARE_ACCELERATED, - gui::WindowInfo::Flag::LAYOUT_IN_OVERSCAN, - gui::WindowInfo::Flag::TRANSLUCENT_STATUS, - gui::WindowInfo::Flag::TRANSLUCENT_NAVIGATION, - gui::WindowInfo::Flag::LOCAL_FOCUS_MODE, - gui::WindowInfo::Flag::SLIPPERY, - gui::WindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR, - gui::WindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS, -}; - -constexpr gui::WindowInfo::Type kType[] = { - gui::WindowInfo::Type::UNKNOWN, - gui::WindowInfo::Type::FIRST_APPLICATION_WINDOW, - gui::WindowInfo::Type::BASE_APPLICATION, - gui::WindowInfo::Type::APPLICATION, - gui::WindowInfo::Type::APPLICATION_STARTING, - gui::WindowInfo::Type::LAST_APPLICATION_WINDOW, - gui::WindowInfo::Type::FIRST_SUB_WINDOW, - gui::WindowInfo::Type::APPLICATION_PANEL, - gui::WindowInfo::Type::APPLICATION_MEDIA, - gui::WindowInfo::Type::APPLICATION_SUB_PANEL, - gui::WindowInfo::Type::APPLICATION_ATTACHED_DIALOG, - gui::WindowInfo::Type::APPLICATION_MEDIA_OVERLAY, -}; - -constexpr gui::WindowInfo::InputConfig kFeatures[] = { - gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL, - gui::WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, - gui::WindowInfo::InputConfig::DROP_INPUT, - gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, - gui::WindowInfo::InputConfig::SPY, - gui::WindowInfo::InputConfig::INTERCEPTS_STYLUS, -}; - -class SurfaceComposerClientFuzzer { -public: - SurfaceComposerClientFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - void invokeSurfaceComposerClient(); - void invokeSurfaceComposerClientBinder(); - void invokeSurfaceComposerTransaction(); - void getWindowInfo(gui::WindowInfo*); - sp<SurfaceControl> makeSurfaceControl(); - BlurRegion getBlurRegion(); - void fuzzOnPullAtom(); - gui::DisplayModeSpecs getDisplayModeSpecs(); - - FuzzedDataProvider mFdp; -}; - -gui::DisplayModeSpecs SurfaceComposerClientFuzzer::getDisplayModeSpecs() { - const auto getRefreshRateRange = [&] { - gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range; - range.min = mFdp.ConsumeFloatingPoint<float>(); - range.max = mFdp.ConsumeFloatingPoint<float>(); - return range; - }; - - const auto getRefreshRateRanges = [&] { - gui::DisplayModeSpecs::RefreshRateRanges ranges; - ranges.physical = getRefreshRateRange(); - ranges.render = getRefreshRateRange(); - return ranges; - }; - - String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); - sp<IBinder> displayToken = - SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/); - gui::DisplayModeSpecs specs; - specs.defaultMode = mFdp.ConsumeIntegral<int32_t>(); - specs.allowGroupSwitching = mFdp.ConsumeBool(); - specs.primaryRanges = getRefreshRateRanges(); - specs.appRequestRanges = getRefreshRateRanges(); - return specs; -} - -BlurRegion SurfaceComposerClientFuzzer::getBlurRegion() { - int32_t left = mFdp.ConsumeIntegral<int32_t>(); - int32_t right = mFdp.ConsumeIntegral<int32_t>(); - int32_t top = mFdp.ConsumeIntegral<int32_t>(); - int32_t bottom = mFdp.ConsumeIntegral<int32_t>(); - uint32_t blurRadius = mFdp.ConsumeIntegral<uint32_t>(); - float alpha = mFdp.ConsumeFloatingPoint<float>(); - float cornerRadiusTL = mFdp.ConsumeFloatingPoint<float>(); - float cornerRadiusTR = mFdp.ConsumeFloatingPoint<float>(); - float cornerRadiusBL = mFdp.ConsumeFloatingPoint<float>(); - float cornerRadiusBR = mFdp.ConsumeFloatingPoint<float>(); - return BlurRegion{blurRadius, cornerRadiusTL, cornerRadiusTR, cornerRadiusBL, - cornerRadiusBR, alpha, left, top, - right, bottom}; -} - -void SurfaceComposerClientFuzzer::getWindowInfo(gui::WindowInfo* windowInfo) { - windowInfo->id = mFdp.ConsumeIntegral<int32_t>(); - windowInfo->name = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes); - windowInfo->layoutParamsFlags = mFdp.PickValueInArray(kFlags); - windowInfo->layoutParamsType = mFdp.PickValueInArray(kType); - windowInfo->frame = Rect(mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<int32_t>(), - mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<int32_t>()); - windowInfo->surfaceInset = mFdp.ConsumeIntegral<int32_t>(); - windowInfo->alpha = mFdp.ConsumeFloatingPointInRange<float>(0, 1); - ui::Transform transform(mFdp.PickValueInArray(kOrientation)); - windowInfo->transform = transform; - windowInfo->touchableRegion = Region(getRect(&mFdp)); - windowInfo->replaceTouchableRegionWithCrop = mFdp.ConsumeBool(); - windowInfo->touchOcclusionMode = mFdp.PickValueInArray(kMode); - windowInfo->ownerPid = gui::Pid{mFdp.ConsumeIntegral<pid_t>()}; - windowInfo->ownerUid = gui::Uid{mFdp.ConsumeIntegral<uid_t>()}; - windowInfo->packageName = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes); - windowInfo->inputConfig = mFdp.PickValueInArray(kFeatures); -} - -sp<SurfaceControl> SurfaceComposerClientFuzzer::makeSurfaceControl() { - sp<IBinder> handle; - const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient()); - sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient); - sp<BnGraphicBufferProducer> producer; - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t transformHint = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t flags = mFdp.ConsumeIntegral<uint32_t>(); - int32_t format = mFdp.ConsumeIntegral<int32_t>(); - int32_t layerId = mFdp.ConsumeIntegral<int32_t>(); - std::string layerName = base::StringPrintf("#%d", layerId); - return new SurfaceControl(client, handle, layerId, layerName, width, height, format, - transformHint, flags); -} - -void SurfaceComposerClientFuzzer::invokeSurfaceComposerTransaction() { - sp<SurfaceControl> surface = makeSurfaceControl(); - - SurfaceComposerClient::Transaction transaction; - int32_t layer = mFdp.ConsumeIntegral<int32_t>(); - transaction.setLayer(surface, layer); - - sp<SurfaceControl> relativeSurface = makeSurfaceControl(); - transaction.setRelativeLayer(surface, relativeSurface, layer); - - Region transparentRegion(getRect(&mFdp)); - transaction.setTransparentRegionHint(surface, transparentRegion); - transaction.setAlpha(surface, mFdp.ConsumeFloatingPoint<float>()); - - transaction.setCornerRadius(surface, mFdp.ConsumeFloatingPoint<float>()); - transaction.setBackgroundBlurRadius(surface, mFdp.ConsumeFloatingPoint<float>()); - std::vector<BlurRegion> regions; - uint32_t vectorSize = mFdp.ConsumeIntegralInRange<uint32_t>(0, 100); - regions.resize(vectorSize); - for (size_t idx = 0; idx < vectorSize; ++idx) { - regions.push_back(getBlurRegion()); - } - transaction.setBlurRegions(surface, regions); - - transaction.setLayerStack(surface, {mFdp.ConsumeIntegral<uint32_t>()}); - half3 color = {mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()}; - transaction.setColor(surface, color); - transaction.setBackgroundColor(surface, color, mFdp.ConsumeFloatingPoint<float>(), - mFdp.PickValueInArray(kDataspaces)); - - transaction.setApi(surface, mFdp.ConsumeIntegral<int32_t>()); - transaction.setFrameRateSelectionPriority(surface, mFdp.ConsumeIntegral<int32_t>()); - transaction.setColorSpaceAgnostic(surface, mFdp.ConsumeBool() /*agnostic*/); - - gui::WindowInfo windowInfo; - getWindowInfo(&windowInfo); - transaction.setInputWindowInfo(surface, windowInfo); - Parcel windowParcel; - windowInfo.writeToParcel(&windowParcel); - windowParcel.setDataPosition(0); - windowInfo.readFromParcel(&windowParcel); - - windowInfo.addTouchableRegion(getRect(&mFdp)); - int32_t pointX = mFdp.ConsumeIntegral<int32_t>(); - int32_t pointY = mFdp.ConsumeIntegral<int32_t>(); - windowInfo.touchableRegionContainsPoint(pointX, pointY); - windowInfo.frameContainsPoint(pointX, pointY); - - Parcel transactionParcel; - transaction.writeToParcel(&transactionParcel); - transactionParcel.setDataPosition(0); - transaction.readFromParcel(&transactionParcel); - SurfaceComposerClient::Transaction::createFromParcel(&transactionParcel); -} - -void SurfaceComposerClientFuzzer::fuzzOnPullAtom() { - std::string outData; - bool success; - SurfaceComposerClient::onPullAtom(mFdp.ConsumeIntegral<int32_t>(), &outData, &success); -} - -void SurfaceComposerClientFuzzer::invokeSurfaceComposerClient() { - String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); - sp<IBinder> displayToken = - SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/); - SurfaceComposerClient::setDesiredDisplayModeSpecs(displayToken, getDisplayModeSpecs()); - - ui::ColorMode colorMode = mFdp.PickValueInArray(kColormodes); - SurfaceComposerClient::setActiveColorMode(displayToken, colorMode); - SurfaceComposerClient::setAutoLowLatencyMode(displayToken, mFdp.ConsumeBool() /*on*/); - SurfaceComposerClient::setGameContentType(displayToken, mFdp.ConsumeBool() /*on*/); - SurfaceComposerClient::setDisplayPowerMode(displayToken, mFdp.ConsumeIntegral<int32_t>()); - SurfaceComposerClient::doUncacheBufferTransaction(mFdp.ConsumeIntegral<uint64_t>()); - - SurfaceComposerClient::setDisplayBrightness(displayToken, getBrightness(&mFdp)); - aidl::android::hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost); - SurfaceComposerClient::notifyPowerBoost((int32_t)boostId); - - String8 surfaceName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); - sp<BBinder> handle(new BBinder()); - sp<BnGraphicBufferProducer> producer; - sp<Surface> surfaceParent( - new Surface(producer, mFdp.ConsumeBool() /*controlledByApp*/, handle)); - - fuzzOnPullAtom(); - SurfaceComposerClient::setDisplayContentSamplingEnabled(displayToken, - mFdp.ConsumeBool() /*enable*/, - mFdp.ConsumeIntegral<uint8_t>(), - mFdp.ConsumeIntegral<uint64_t>()); - - sp<IBinder> stopLayerHandle; - sp<gui::IRegionSamplingListener> listener = sp<gui::IRegionSamplingListenerDefault>::make(); - sp<gui::IRegionSamplingListenerDelegator> sampleListener = - new gui::IRegionSamplingListenerDelegator(listener); - SurfaceComposerClient::addRegionSamplingListener(getRect(&mFdp), stopLayerHandle, - sampleListener); - sp<gui::IFpsListenerDefault> fpsListener; - SurfaceComposerClient::addFpsListener(mFdp.ConsumeIntegral<int32_t>(), fpsListener); -} - -void SurfaceComposerClientFuzzer::invokeSurfaceComposerClientBinder() { - sp<FakeBnSurfaceComposerClient> client(new FakeBnSurfaceComposerClient()); - fuzzService(client.get(), std::move(mFdp)); -} - -void SurfaceComposerClientFuzzer::process() { - invokeSurfaceComposerClient(); - invokeSurfaceComposerTransaction(); - invokeSurfaceComposerClientBinder(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - SurfaceComposerClientFuzzer surfaceComposerClientFuzzer(data, size); - surfaceComposerClientFuzzer.process(); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp deleted file mode 100644 index 6d5427bc9e..0000000000 --- a/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2021 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 <fuzzbinder/libbinder_driver.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <libgui_fuzzer_utils.h> - -using namespace android; - -class SurfaceComposerFuzzer { -public: - SurfaceComposerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - FuzzedDataProvider mFdp; -}; - -void SurfaceComposerFuzzer::process() { - sp<FakeBnSurfaceComposer> composer(new FakeBnSurfaceComposer()); - fuzzService(composer.get(), std::move(mFdp)); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - SurfaceComposerFuzzer surfaceComposerFuzzer(data, size); - surfaceComposerFuzzer.process(); - return 0; -} diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index 9fef512b64..55a7aa7ddc 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -116,6 +116,8 @@ private: void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) override; + void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) override; void scheduleCallbacks(); diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index fe2dd206ed..82cd50c7bd 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -65,6 +65,9 @@ private: virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) = 0; + virtual void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) = 0; + bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, VsyncEventData* outVsyncEventData); diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 79582ce685..8c1103bfc2 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -58,7 +58,6 @@ static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) { // ---------------------------------------------------------------------------- class DisplayEventReceiver { public: - enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), @@ -66,6 +65,7 @@ public: DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'), DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'), DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'), + DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'), }; struct Event { @@ -101,12 +101,22 @@ public: float frameRateHz __attribute__((aligned(8))); }; + /* + * The values are defined in aidl: + * hardware/interfaces/drm/aidl/android/hardware/drm/HdcpLevel.aidl + */ + struct HdcpLevelsChange { + int32_t connectedLevel; + int32_t maxLevel; + }; + Header header; union { VSync vsync; Hotplug hotplug; ModeChange modeChange; FrameRateOverride frameRateOverride; + HdcpLevelsChange hdcpLevelsChange; }; }; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index e1dc7911a1..0fedea7b9e 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -161,6 +161,9 @@ struct layer_state_t { // See SurfaceView scaling behavior for more details. eIgnoreDestinationFrame = 0x400, eLayerIsRefreshRateIndicator = 0x800, // REFRESH_RATE_INDICATOR + // Sets a property on this layer indicating that its visible region should be considered + // when computing TrustedPresentation Thresholds. + eCanOccludePresentation = 0x1000, }; enum { @@ -206,7 +209,7 @@ struct layer_state_t { eBackgroundBlurRadiusChanged = 0x80'00000000, eProducerDisconnect = 0x100'00000000, eFixedTransformHintChanged = 0x200'00000000, - /* unused 0x400'00000000, */ + eDesiredHdrHeadroomChanged = 0x400'00000000, eBlurRegionsChanged = 0x800'00000000, eAutoRefreshChanged = 0x1000'00000000, eStretchChanged = 0x2000'00000000, @@ -245,7 +248,8 @@ struct layer_state_t { layer_state_t::eSidebandStreamChanged | layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eTransparentRegionChanged | - layer_state_t::eExtendedRangeBrightnessChanged; + layer_state_t::eExtendedRangeBrightnessChanged | + layer_state_t::eDesiredHdrHeadroomChanged; // Content updates. static constexpr uint64_t CONTENT_CHANGES = layer_state_t::BUFFER_CHANGES | @@ -416,26 +420,36 @@ public: }; struct DisplayState { - enum { + enum : uint32_t { eSurfaceChanged = 0x01, eLayerStackChanged = 0x02, eDisplayProjectionChanged = 0x04, eDisplaySizeChanged = 0x08, - eFlagsChanged = 0x10 + eFlagsChanged = 0x10, + + eAllChanged = ~0u }; + // Not for direct use. Prefer constructor below for new displays. DisplayState(); + + DisplayState(sp<IBinder> token, ui::LayerStack layerStack) + : what(eAllChanged), + token(std::move(token)), + layerStack(layerStack), + layerStackSpaceRect(Rect::INVALID_RECT), + orientedDisplaySpaceRect(Rect::INVALID_RECT) {} + void merge(const DisplayState& other); void sanitize(int32_t permissions); uint32_t what = 0; uint32_t flags = 0; sp<IBinder> token; - sp<IGraphicBufferProducer> surface; ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK; - // These states define how layers are projected onto the physical display. + // These states define how layers are projected onto the physical or virtual display. // // Layers are first clipped to `layerStackSpaceRect'. They are then translated and // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated @@ -446,10 +460,17 @@ struct DisplayState { // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W, // 0). + // + // Rect::INVALID_RECT sizes the space to the active resolution of the physical display, or the + // default dimensions of the virtual display surface. + // ui::Rotation orientation = ui::ROTATION_0; Rect layerStackSpaceRect = Rect::EMPTY_RECT; Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT; + // Exclusive to virtual displays: The sink surface into which the virtual display is rendered, + // and an optional resolution that overrides its default dimensions. + sp<IGraphicBufferProducer> surface; uint32_t width = 0; uint32_t height = 0; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 14e3dd583e..288882695a 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -594,6 +594,7 @@ public: Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc, float currentBufferRatio, float desiredRatio); + Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio); Transaction& setCachingHint(const sp<SurfaceControl>& sc, gui::CachingHint cachingHint); Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc, @@ -849,7 +850,8 @@ public: static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); static status_t captureDisplay(DisplayId, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&); - static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); + static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&, + bool sync); [[deprecated]] static status_t captureDisplay(DisplayId id, const sp<IScreenCaptureListener>& listener) { diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 4d4c5e4394..32d60be612 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -176,6 +176,8 @@ struct WindowInfo : public Parcelable { static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS), CLONE = static_cast<uint32_t>(os::InputConfig::CLONE), + GLOBAL_STYLUS_BLOCKS_TOUCH = + static_cast<uint32_t>(os::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH), // clang-format on }; @@ -244,6 +246,10 @@ struct WindowInfo : public Parcelable { // any other window. sp<IBinder> focusTransferTarget; + // Sets a property on this window indicating that its visible region should be considered when + // computing TrustedPresentation Thresholds. + bool canOccludePresentation = false; + void setInputConfig(ftl::Flags<InputConfig> config, bool value); void addTouchableRegion(const Rect& region); @@ -267,6 +273,8 @@ struct WindowInfo : public Parcelable { status_t readFromParcel(const android::Parcel* parcel) override; }; +std::ostream& operator<<(std::ostream& out, const WindowInfo& window); + /* * Handle for a window that can receive input. * diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp index cc33e4c27d..386767ba9e 100644 --- a/libs/gui/sysprop/Android.bp +++ b/libs/gui/sysprop/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } sysprop_library { diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index d4b8dbeeb9..a9d6e8d3bf 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -63,8 +63,7 @@ namespace android::test { using Transaction = SurfaceComposerClient::Transaction; sp<IInputFlinger> getInputFlinger() { - sp<IBinder> input(defaultServiceManager()->getService( - String16("inputflinger"))); + sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); if (input == nullptr) { ALOGE("Failed to link to input service"); } else { ALOGE("Linked to input"); } @@ -104,8 +103,13 @@ public: if (noInputChannel) { mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); } else { - mClientChannel = std::make_shared<InputChannel>(); - mInputFlinger->createInputChannel("testchannels", mClientChannel.get()); + android::os::InputChannelCore tempChannel; + android::binder::Status result = + mInputFlinger->createInputChannel("testchannels", &tempChannel); + if (!result.isOk()) { + ADD_FAILURE() << "binder call to createInputChannel failed"; + } + mClientChannel = InputChannel::create(std::move(tempChannel)); mInputInfo.token = mClientChannel->getConnectionToken(); mInputConsumer = new InputConsumer(mClientChannel); } @@ -168,8 +172,8 @@ public: return std::make_unique<InputSurface>(surfaceControl, width, height); } - InputEvent *consumeEvent(int timeoutMs = 3000) { - waitForEventAvailable(timeoutMs); + InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) { + mClientChannel->waitForMessage(timeout); InputEvent *ev; uint32_t seqId; @@ -302,15 +306,6 @@ public: t.apply(true); } -private: - void waitForEventAvailable(int timeoutMs) { - struct pollfd fd; - - fd.fd = mClientChannel->getFd(); - fd.events = POLLIN; - poll(&fd, 1, timeoutMs); - } - public: sp<SurfaceControl> mSurfaceControl; std::shared_ptr<InputChannel> mClientChannel; @@ -615,7 +610,7 @@ TEST_F(InputSurfacesTest, touchable_region) { // A tap within the surface but outside the touchable region should not be sent to the surface. injectTap(20, 30); - EXPECT_EQ(surface->consumeEvent(200 /*timeoutMs*/), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr); injectTap(31, 52); surface->expectTap(20, 30); @@ -981,12 +976,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; obscuringSurface->showAt(100, 100); injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { @@ -1002,12 +997,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { @@ -1024,12 +1019,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { @@ -1046,12 +1041,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { injectTap(111, 111); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) { @@ -1076,12 +1071,12 @@ TEST_F(InputSurfacesTest, drop_input_policy) { injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) { @@ -1116,7 +1111,7 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ // Does not receive events outside its crop injectTap(26, 26); - EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); + EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); } /** @@ -1141,7 +1136,7 @@ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_nul // Does not receive events outside parent bounds injectTap(31, 31); - EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); + EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); } /** @@ -1167,7 +1162,7 @@ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { // Does not receive events outside crop layer bounds injectTap(21, 21); injectTap(71, 71); - EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); + EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) { @@ -1184,7 +1179,7 @@ TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) { [&](auto &t, auto &sc) { t.reparent(sc, parent->mSurfaceControl); }); injectTap(101, 101); - EXPECT_EQ(parent->consumeEvent(100), nullptr); + EXPECT_EQ(parent->consumeEvent(/*timeout=*/100ms), nullptr); } class MultiDisplayTests : public InputSurfacesTest { @@ -1233,7 +1228,7 @@ TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) { // Touches should be dropped if the layer is on an invalid display. injectTapOnDisplay(101, 101, layerStack.id); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); // However, we still let the window be focused and receive keys. surface->requestFocus(layerStack.id); @@ -1271,12 +1266,12 @@ TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) { injectTapOnDisplay(101, 101, layerStack.id); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(layerStack.id); surface->assertFocusChange(true); injectKeyOnDisplay(AKEYCODE_V, layerStack.id); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) { diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp index 5fe22b05f9..04bfb28f65 100644 --- a/libs/gui/tests/FrameRateUtilsTest.cpp +++ b/libs/gui/tests/FrameRateUtilsTest.cpp @@ -34,6 +34,8 @@ TEST(FrameRateUtilsTest, ValidateFrameRate) { ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, "")); EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_GTE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); // Privileged APIs. EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index c6ea317949..577d2394c6 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -791,6 +791,10 @@ public: return binder::Status::ok(); } + binder::Status captureLayersSync(const LayerCaptureArgs&, ScreenCaptureResults*) override { + return binder::Status::ok(); + } + binder::Status captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override { return binder::Status::ok(); diff --git a/libs/input/AccelerationCurve.cpp b/libs/input/AccelerationCurve.cpp new file mode 100644 index 0000000000..0a92a71596 --- /dev/null +++ b/libs/input/AccelerationCurve.cpp @@ -0,0 +1,63 @@ +/* + * 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 <input/AccelerationCurve.h> + +#include <array> +#include <limits> + +#include <log/log_main.h> + +#define LOG_TAG "AccelerationCurve" + +namespace android { + +namespace { + +// The last segment must have an infinite maximum speed, so that all speeds are covered. +constexpr std::array<AccelerationCurveSegment, 4> kSegments = {{ + {32.002, 3.19, 0}, + {52.83, 4.79, -51.254}, + {119.124, 7.28, -182.737}, + {std::numeric_limits<double>::infinity(), 15.04, -1107.556}, +}}; + +static_assert(kSegments.back().maxPointerSpeedMmPerS == std::numeric_limits<double>::infinity()); + +constexpr std::array<double, 15> kSensitivityFactors = {1, 2, 4, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 16, 18, 20}; + +} // namespace + +std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivity( + int32_t sensitivity) { + LOG_ALWAYS_FATAL_IF(sensitivity < -7 || sensitivity > 7, "Invalid pointer sensitivity value"); + std::vector<AccelerationCurveSegment> output; + output.reserve(kSegments.size()); + + // The curves we want to produce for different sensitivity values are actually the same curve, + // just scaled in the Y (gain) axis by a sensitivity factor and a couple of constants. + double commonFactor = 0.64 * kSensitivityFactors[sensitivity + 7] / 10; + for (AccelerationCurveSegment seg : kSegments) { + output.push_back(AccelerationCurveSegment{seg.maxPointerSpeedMmPerS, + commonFactor * seg.baseGain, + commonFactor * seg.reciprocal}); + } + + return output; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 400ca9fa7d..2b5d3758be 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -176,6 +176,8 @@ cc_library { ], srcs: [ "android/os/IInputFlinger.aidl", + "android/os/InputChannelCore.aidl", + "AccelerationCurve.cpp", "Input.cpp", "InputDevice.cpp", "InputEventLabels.cpp", diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index bd5b67b1d0..9e0ce1db33 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -374,11 +374,16 @@ std::ostream& operator<<(std::ostream& out, const KeyEvent& event) { out << ", deviceId=" << event.getDeviceId(); out << ", source=" << inputEventSourceToString(event.getSource()); out << ", displayId=" << event.getDisplayId(); - out << ", eventId=" << event.getId(); + out << ", eventId=0x" << std::hex << event.getId() << std::dec; out << "}"; return out; } +std::ostream& operator<<(std::ostream& out, const PointerProperties& properties) { + out << "Pointer(id=" << properties.id << ", " << ftl::enum_string(properties.toolType) << ")"; + return out; +} + // --- PointerCoords --- float PointerCoords::getAxisValue(int32_t axis) const { @@ -1002,6 +1007,33 @@ PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, return out; } +bool MotionEvent::operator==(const android::MotionEvent& o) const { + // We use NaN values to represent invalid cursor positions. Since NaN values are not equal + // to themselves according to IEEE 754, we cannot use the default equality operator to compare + // MotionEvents. Therefore we define a custom equality operator with special handling for NaNs. + // clang-format off + return InputEvent::operator==(static_cast<const InputEvent&>(o)) && + mAction == o.mAction && + mActionButton == o.mActionButton && + mFlags == o.mFlags && + mEdgeFlags == o.mEdgeFlags && + mMetaState == o.mMetaState && + mButtonState == o.mButtonState && + mClassification == o.mClassification && + mTransform == o.mTransform && + mXPrecision == o.mXPrecision && + mYPrecision == o.mYPrecision && + ((std::isnan(mRawXCursorPosition) && std::isnan(o.mRawXCursorPosition)) || + mRawXCursorPosition == o.mRawXCursorPosition) && + ((std::isnan(mRawYCursorPosition) && std::isnan(o.mRawYCursorPosition)) || + mRawYCursorPosition == o.mRawYCursorPosition) && + mRawTransform == o.mRawTransform && mDownTime == o.mDownTime && + mPointerProperties == o.mPointerProperties && + mSampleEventTimes == o.mSampleEventTimes && + mSamplePointerCoords == o.mSamplePointerCoords; + // clang-format on +} + std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction()); if (event.getActionButton() != 0) { @@ -1032,6 +1064,9 @@ std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { if (event.getMetaState() != 0) { out << ", metaState=" << event.getMetaState(); } + if (event.getFlags() != 0) { + out << ", flags=0x" << std::hex << event.getFlags() << std::dec; + } if (event.getEdgeFlags() != 0) { out << ", edgeFlags=" << event.getEdgeFlags(); } @@ -1046,7 +1081,7 @@ std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << ", deviceId=" << event.getDeviceId(); out << ", source=" << inputEventSourceToString(event.getSource()); out << ", displayId=" << event.getDisplayId(); - out << ", eventId=" << event.getId(); + out << ", eventId=0x" << std::hex << event.getId() << std::dec; out << "}"; return out; } diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index ccc83231fa..c348833747 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -193,14 +193,16 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mHasSensor(other.mHasSensor), mMotionRanges(other.mMotionRanges), mSensors(other.mSensors), - mLights(other.mLights) {} + mLights(other.mLights), + mViewBehavior(other.mViewBehavior) {} InputDeviceInfo::~InputDeviceInfo() { } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, int32_t associatedDisplayId) { + bool isExternal, bool hasMic, int32_t associatedDisplayId, + InputDeviceViewBehavior viewBehavior) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; @@ -215,6 +217,7 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mHasBattery = false; mHasButtonUnderPad = false; mHasSensor = false; + mViewBehavior = viewBehavior; mUsiVersion.reset(); mMotionRanges.clear(); mSensors.clear(); diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index 0e627e56fd..8db0ca588b 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -348,7 +348,9 @@ namespace android { DEFINE_KEYCODE(MACRO_1), \ DEFINE_KEYCODE(MACRO_2), \ DEFINE_KEYCODE(MACRO_3), \ - DEFINE_KEYCODE(MACRO_4) + DEFINE_KEYCODE(MACRO_4), \ + DEFINE_KEYCODE(EMOJI_PICKER), \ + DEFINE_KEYCODE(SCREENSHOT) // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 09e98d0515..e49f4eb6f6 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -10,6 +10,7 @@ #include <fcntl.h> #include <inttypes.h> #include <math.h> +#include <poll.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> @@ -94,6 +95,21 @@ bool debugResampling() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); } +android::base::unique_fd dupChannelFd(int fd) { + android::base::unique_fd newFd(::dup(fd)); + if (!newFd.ok()) { + ALOGE("Could not duplicate fd %i : %s", fd, strerror(errno)); + const bool hitFdLimit = errno == EMFILE || errno == ENFILE; + // If this process is out of file descriptors, then throwing that might end up exploding + // on the other side of a binder call, which isn't really helpful. + // Better to just crash here and hope that the FD leak is slow. + // Other failures could be client errors, so we still propagate those back to the caller. + LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel"); + return {}; + } + return newFd; +} + } // namespace using android::base::Result; @@ -394,15 +410,23 @@ std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token)); } -InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) - : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) { +std::unique_ptr<InputChannel> InputChannel::create( + android::os::InputChannelCore&& parceledChannel) { + return InputChannel::create(parceledChannel.name, parceledChannel.fd.release(), + parceledChannel.token); +} + +InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) { + this->name = std::move(name); + this->fd.reset(std::move(fd)); + this->token = std::move(token); ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel constructed: name='%s', fd=%d", - getName().c_str(), getFd().get()); + getName().c_str(), getFd()); } InputChannel::~InputChannel() { ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel destroyed: name='%s', fd=%d", - getName().c_str(), getFd().get()); + getName().c_str(), getFd()); } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -440,19 +464,19 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { ATRACE_NAME_IF(ATRACE_ENABLED(), StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")", - mName.c_str(), msg->header.seq, msg->header.type)); + name.c_str(), msg->header.seq, msg->header.type)); const size_t msgLength = msg->size(); InputMessage cleanMsg; msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(getFd().get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { int error = errno; ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ error sending message of type %s, %s", - mName.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); + name.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -464,12 +488,12 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { if (size_t(nWrite) != msgLength) { ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ error sending message type %s, send was incomplete", mName.c_str(), + "channel '%s' ~ error sending message type %s, send was incomplete", name.c_str(), ftl::enum_string(msg->header.type).c_str()); return DEAD_OBJECT; } - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(), + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", name.c_str(), ftl::enum_string(msg->header.type).c_str()); return OK; @@ -478,13 +502,13 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::recv(getFd().get(), msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { int error = errno; ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d", - mName.c_str(), errno); + name.c_str(), errno); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -496,81 +520,85 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { if (nRead == 0) { // check for EOF ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ receive message failed because peer was closed", mName.c_str()); + "channel '%s' ~ receive message failed because peer was closed", name.c_str()); return DEAD_OBJECT; } if (!msg->isValid(nRead)) { - ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead); + ALOGE("channel '%s' ~ received invalid message of size %zd", name.c_str(), nRead); return BAD_VALUE; } - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(), + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", name.c_str(), ftl::enum_string(msg->header.type).c_str()); if (ATRACE_ENABLED()) { // Add an additional trace point to include data about the received message. std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")", - mName.c_str(), msg->header.seq, msg->header.type); + name.c_str(), msg->header.seq, msg->header.type); ATRACE_NAME(message.c_str()); } return OK; } -std::unique_ptr<InputChannel> InputChannel::dup() const { - base::unique_fd newFd(dupFd()); - return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); +bool InputChannel::probablyHasInput() const { + struct pollfd pfds = {.fd = fd.get(), .events = POLLIN}; + if (::poll(&pfds, /*nfds=*/1, /*timeout=*/0) <= 0) { + // This can be a false negative because EINTR and ENOMEM are not handled. The latter should + // be extremely rare. The EINTR is also unlikely because it happens only when the signal + // arrives while the syscall is executed, and the syscall is quick. Hitting EINTR too often + // would be a sign of having too many signals, which is a bigger performance problem. A + // common tradition is to repeat the syscall on each EINTR, but it is not necessary here. + // In other words, the missing one liner is replaced by a multiline explanation. + return false; + } + // From poll(2): The bits returned in |revents| can include any of those specified in |events|, + // or one of the values POLLERR, POLLHUP, or POLLNVAL. + return (pfds.revents & POLLIN) != 0; } -void InputChannel::copyTo(InputChannel& outChannel) const { - outChannel.mName = getName(); - outChannel.mFd = dupFd(); - outChannel.mToken = getConnectionToken(); +void InputChannel::waitForMessage(std::chrono::milliseconds timeout) const { + if (timeout < 0ms) { + LOG(FATAL) << "Timeout cannot be negative, received " << timeout.count(); + } + struct pollfd pfds = {.fd = fd.get(), .events = POLLIN}; + int ret; + std::chrono::time_point<std::chrono::steady_clock> stopTime = + std::chrono::steady_clock::now() + timeout; + std::chrono::milliseconds remaining = timeout; + do { + ret = ::poll(&pfds, /*nfds=*/1, /*timeout=*/remaining.count()); + remaining = std::chrono::duration_cast<std::chrono::milliseconds>( + stopTime - std::chrono::steady_clock::now()); + } while (ret == -1 && errno == EINTR && remaining > 0ms); } -status_t InputChannel::writeToParcel(android::Parcel* parcel) const { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - return parcel->writeStrongBinder(mToken) - ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd); +std::unique_ptr<InputChannel> InputChannel::dup() const { + base::unique_fd newFd(dupChannelFd(fd.get())); + return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); } -status_t InputChannel::readFromParcel(const android::Parcel* parcel) { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - mToken = parcel->readStrongBinder(); - return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd); +void InputChannel::copyTo(android::os::InputChannelCore& outChannel) const { + outChannel.name = getName(); + outChannel.fd.reset(dupChannelFd(fd.get())); + outChannel.token = getConnectionToken(); } -sp<IBinder> InputChannel::getConnectionToken() const { - return mToken; +void InputChannel::moveChannel(std::unique_ptr<InputChannel> from, + android::os::InputChannelCore& outChannel) { + outChannel.name = from->getName(); + outChannel.fd = android::os::ParcelFileDescriptor(std::move(from->fd)); + outChannel.token = from->getConnectionToken(); } -base::unique_fd InputChannel::dupFd() const { - base::unique_fd newFd(::dup(getFd().get())); - if (!newFd.ok()) { - ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(), - strerror(errno)); - const bool hitFdLimit = errno == EMFILE || errno == ENFILE; - // If this process is out of file descriptors, then throwing that might end up exploding - // on the other side of a binder call, which isn't really helpful. - // Better to just crash here and hope that the FD leak is slow. - // Other failures could be client errors, so we still propagate those back to the caller. - LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", - getName().c_str()); - return {}; - } - return newFd; +sp<IBinder> InputChannel::getConnectionToken() const { + return token; } // --- InputPublisher --- InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) - : mChannel(channel), mInputVerifier(channel->getName()) {} + : mChannel(channel), mInputVerifier(mChannel->getName()) {} InputPublisher::~InputPublisher() { } @@ -646,7 +674,7 @@ status_t InputPublisher::publishMotionEvent( "action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, classification=%s," "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " - "pointerCount=%" PRIu32 " \n%s", + "pointerCount=%" PRIu32 "\n%s", mChannel->getName().c_str(), __func__, seq, eventId, deviceId, inputEventSourceToString(source).c_str(), displayId, MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags, @@ -850,6 +878,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, mMsg.header.seq); + + // Trace the event processing timeline - event was just read from the socket + ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/mMsg.header.seq); } if (result) { // Consume the next batched event unless batches are being held for later. @@ -1388,6 +1419,9 @@ status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) // message anymore. If the socket write did not succeed, we will try again and will still // need consume time. popConsumeTime(seq); + + // Trace the event processing timeline - event was just finished + ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/seq); } return result; } @@ -1406,6 +1440,10 @@ int32_t InputConsumer::getPendingBatchSource() const { return head.body.motion.source; } +bool InputConsumer::probablyHasInput() const { + return hasPendingBatch() || mChannel->probablyHasInput(); +} + ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mBatches.size(); i++) { const Batch& batch = mBatches[i]; diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index ab8c341b15..508818852e 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -97,6 +97,10 @@ static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_ bool kernelConfigsArePresent(const std::set<std::string>& configs) { #if defined(__ANDROID__) + if (configs.empty()) { + return true; + } + std::map<std::string, std::string> kernelConfigs; const status_t result = android::kernelconfigs::LoadKernelConfigs(&kernelConfigs); LOG_ALWAYS_FATAL_IF(result != OK, "Kernel configs could not be fetched"); diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index c835a081a5..edd31e91d3 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -15,7 +15,6 @@ */ #define LOG_TAG "VelocityControl" -//#define LOG_NDEBUG 0 // Log debug messages about acceleration. static constexpr bool DEBUG_ACCELERATION = false; @@ -23,6 +22,7 @@ static constexpr bool DEBUG_ACCELERATION = false; #include <math.h> #include <limits.h> +#include <android-base/logging.h> #include <input/VelocityControl.h> #include <utils/BitSet.h> #include <utils/Timers.h> @@ -37,15 +37,6 @@ VelocityControl::VelocityControl() { reset(); } -const VelocityControlParameters& VelocityControl::getParameters() const{ - return mParameters; -} - -void VelocityControl::setParameters(const VelocityControlParameters& parameters) { - mParameters = parameters; - reset(); -} - void VelocityControl::reset() { mLastMovementTime = LLONG_MIN; mRawPositionX = 0; @@ -54,65 +45,156 @@ void VelocityControl::reset() { } void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { - if ((deltaX && *deltaX) || (deltaY && *deltaY)) { - if (eventTime >= mLastMovementTime + STOP_TIME) { - if (DEBUG_ACCELERATION && mLastMovementTime != LLONG_MIN) { - ALOGD("VelocityControl: stopped, last movement was %0.3fms ago", - (eventTime - mLastMovementTime) * 0.000001f); - } - reset(); - } + if ((deltaX == nullptr || *deltaX == 0) && (deltaY == nullptr || *deltaY == 0)) { + return; + } + if (eventTime >= mLastMovementTime + STOP_TIME) { + ALOGD_IF(DEBUG_ACCELERATION && mLastMovementTime != LLONG_MIN, + "VelocityControl: stopped, last movement was %0.3fms ago", + (eventTime - mLastMovementTime) * 0.000001f); + reset(); + } - mLastMovementTime = eventTime; - if (deltaX) { - mRawPositionX += *deltaX; - } - if (deltaY) { - mRawPositionY += *deltaY; - } - mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X, - mRawPositionX); - mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y, - mRawPositionY); - - std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); - std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); - float scale = mParameters.scale; - if (vx && vy) { - float speed = hypotf(*vx, *vy) * scale; - if (speed >= mParameters.highThreshold) { - // Apply full acceleration above the high speed threshold. - scale *= mParameters.acceleration; - } else if (speed > mParameters.lowThreshold) { - // Linearly interpolate the acceleration to apply between the low and high - // speed thresholds. - scale *= 1 + (speed - mParameters.lowThreshold) - / (mParameters.highThreshold - mParameters.lowThreshold) - * (mParameters.acceleration - 1); - } - - if (DEBUG_ACCELERATION) { - ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " - "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale); - } - - } else { - if (DEBUG_ACCELERATION) { - ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration); - } - } + mLastMovementTime = eventTime; + if (deltaX) { + mRawPositionX += *deltaX; + } + if (deltaY) { + mRawPositionY += *deltaY; + } + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X, mRawPositionX); + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y, mRawPositionY); + scaleDeltas(deltaX, deltaY); +} + +// --- SimpleVelocityControl --- + +const VelocityControlParameters& SimpleVelocityControl::getParameters() const { + return mParameters; +} - if (deltaX) { - *deltaX *= scale; +void SimpleVelocityControl::setParameters(const VelocityControlParameters& parameters) { + mParameters = parameters; + reset(); +} + +void SimpleVelocityControl::scaleDeltas(float* deltaX, float* deltaY) { + std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); + std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); + float scale = mParameters.scale; + if (vx.has_value() && vy.has_value()) { + float speed = hypotf(*vx, *vy) * scale; + if (speed >= mParameters.highThreshold) { + // Apply full acceleration above the high speed threshold. + scale *= mParameters.acceleration; + } else if (speed > mParameters.lowThreshold) { + // Linearly interpolate the acceleration to apply between the low and high + // speed thresholds. + scale *= 1 + + (speed - mParameters.lowThreshold) / + (mParameters.highThreshold - mParameters.lowThreshold) * + (mParameters.acceleration - 1); } - if (deltaY) { - *deltaY *= scale; + + ALOGD_IF(DEBUG_ACCELERATION, + "SimpleVelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " + "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale); + + } else { + ALOGD_IF(DEBUG_ACCELERATION, + "SimpleVelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration); + } + + if (deltaX != nullptr) { + *deltaX *= scale; + } + if (deltaY != nullptr) { + *deltaY *= scale; + } +} + +// --- CurvedVelocityControl --- + +namespace { + +/** + * The resolution that we assume a mouse to have, in counts per inch. + * + * Mouse resolutions vary wildly, but 800 CPI is probably the most common. There should be enough + * range in the available sensitivity settings to accommodate users of mice with other resolutions. + */ +constexpr int32_t MOUSE_CPI = 800; + +float countsToMm(float counts) { + return counts / MOUSE_CPI * 25.4; +} + +} // namespace + +CurvedVelocityControl::CurvedVelocityControl() + : mCurveSegments(createAccelerationCurveForPointerSensitivity(0)) {} + +void CurvedVelocityControl::setCurve(const std::vector<AccelerationCurveSegment>& curve) { + mCurveSegments = curve; +} + +void CurvedVelocityControl::setAccelerationEnabled(bool enabled) { + mAccelerationEnabled = enabled; +} + +void CurvedVelocityControl::scaleDeltas(float* deltaX, float* deltaY) { + if (!mAccelerationEnabled) { + ALOGD_IF(DEBUG_ACCELERATION, "CurvedVelocityControl: acceleration disabled"); + return; + } + + std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); + std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); + + float ratio; + if (vx.has_value() && vy.has_value()) { + float vxMmPerS = countsToMm(*vx); + float vyMmPerS = countsToMm(*vy); + float speedMmPerS = sqrtf(vxMmPerS * vxMmPerS + vyMmPerS * vyMmPerS); + + const AccelerationCurveSegment& seg = segmentForSpeed(speedMmPerS); + ratio = seg.baseGain + seg.reciprocal / speedMmPerS; + ALOGD_IF(DEBUG_ACCELERATION, + "CurvedVelocityControl: velocities (%0.3f, %0.3f) → speed %0.3f → ratio %0.3f", + vxMmPerS, vyMmPerS, speedMmPerS, ratio); + } else { + // We don't have enough data to compute a velocity yet. This happens early in the movement, + // when the speed is presumably low, so use the base gain of the first segment of the curve. + // (This would behave oddly for curves with a reciprocal term on the first segment, but we + // don't have any of those, and they'd be very strange at velocities close to zero anyway.) + ratio = mCurveSegments[0].baseGain; + ALOGD_IF(DEBUG_ACCELERATION, + "CurvedVelocityControl: unknown velocity, using base gain of first segment (%.3f)", + ratio); + } + + if (deltaX != nullptr) { + *deltaX *= ratio; + } + if (deltaY != nullptr) { + *deltaY *= ratio; + } +} + +const AccelerationCurveSegment& CurvedVelocityControl::segmentForSpeed(float speedMmPerS) { + for (const AccelerationCurveSegment& seg : mCurveSegments) { + if (speedMmPerS <= seg.maxPointerSpeedMmPerS) { + return seg; } } + ALOGE("CurvedVelocityControl: No segment found for speed %.3f; last segment should always have " + "a max speed of infinity.", + speedMmPerS); + return mCurveSegments.back(); } } // namespace android diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp index db7031ab03..eea06f1720 100644 --- a/libs/input/VirtualInputDevice.cpp +++ b/libs/input/VirtualInputDevice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright 2023 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. @@ -39,7 +39,9 @@ static bool isDebug() { } namespace android { + VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {} + VirtualInputDevice::~VirtualInputDevice() { ioctl(mFd, UI_DEV_DESTROY); } @@ -56,7 +58,7 @@ bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t v return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev); } -/** Utility method to write keyboard key events or mouse button events. */ +/** Utility method to write keyboard key events or mouse/stylus button events. */ bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAction, const std::map<int, int>& evKeyCodeMapping, const std::map<int, UinputAction>& actionMapping, @@ -68,13 +70,17 @@ bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAct } auto actionIterator = actionMapping.find(androidAction); if (actionIterator == actionMapping.end()) { + ALOGE("Unsupported native action for android action %d", androidAction); return false; } - if (!writeInputEvent(EV_KEY, static_cast<uint16_t>(evKeyCodeIterator->second), - static_cast<int32_t>(actionIterator->second), eventTime)) { + int32_t action = static_cast<int32_t>(actionIterator->second); + uint16_t evKeyCode = static_cast<uint16_t>(evKeyCodeIterator->second); + if (!writeInputEvent(EV_KEY, evKeyCode, action, eventTime)) { + ALOGE("Failed to write native action %d and EV keycode %u.", action, evKeyCode); return false; } if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) { + ALOGE("Failed to write SYN_REPORT for EV_KEY event."); return false; } return true; @@ -85,6 +91,7 @@ const std::map<int, UinputAction> VirtualKeyboard::KEY_ACTION_MAPPING = { {AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS}, {AKEY_EVENT_ACTION_UP, UinputAction::RELEASE}, }; + // Keycode mapping from https://source.android.com/devices/input/keyboard-devices const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = { {AKEYCODE_0, KEY_0}, @@ -195,7 +202,9 @@ const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = { {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA}, {AKEYCODE_LANGUAGE_SWITCH, KEY_LANGUAGE}, }; + VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {} + VirtualKeyboard::~VirtualKeyboard() {} bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction, @@ -275,6 +284,7 @@ const std::map<int, UinputAction> VirtualTouchscreen::TOUCH_ACTION_MAPPING = { {AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE}, {AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL}, }; + // Tool type mapping from https://source.android.com/devices/input/touch-devices const std::map<int, int> VirtualTouchscreen::TOOL_TYPE_MAPPING = { {AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER}, @@ -393,4 +403,110 @@ bool VirtualTouchscreen::handleTouchDown(int32_t pointerId, std::chrono::nanosec return true; } +// --- VirtualStylus --- +const std::map<int, int> VirtualStylus::TOOL_TYPE_MAPPING = { + {AMOTION_EVENT_TOOL_TYPE_STYLUS, BTN_TOOL_PEN}, + {AMOTION_EVENT_TOOL_TYPE_ERASER, BTN_TOOL_RUBBER}, +}; + +// Button code mapping from https://source.android.com/devices/input/touch-devices +const std::map<int, int> VirtualStylus::BUTTON_CODE_MAPPING = { + {AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, BTN_STYLUS}, + {AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, BTN_STYLUS2}, +}; + +VirtualStylus::VirtualStylus(unique_fd fd) + : VirtualInputDevice(std::move(fd)), mIsStylusDown(false) {} + +VirtualStylus::~VirtualStylus() {} + +bool VirtualStylus::writeMotionEvent(int32_t toolType, int32_t action, int32_t locationX, + int32_t locationY, int32_t pressure, int32_t tiltX, + int32_t tiltY, std::chrono::nanoseconds eventTime) { + auto actionIterator = VirtualTouchscreen::TOUCH_ACTION_MAPPING.find(action); + if (actionIterator == VirtualTouchscreen::TOUCH_ACTION_MAPPING.end()) { + ALOGE("Unsupported action passed for stylus: %d.", action); + return false; + } + UinputAction uinputAction = actionIterator->second; + auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType); + if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) { + ALOGE("Unsupported tool type passed for stylus: %d.", toolType); + return false; + } + uint16_t tool = static_cast<uint16_t>(toolTypeIterator->second); + if (uinputAction == UinputAction::PRESS && !handleStylusDown(tool, eventTime)) { + return false; + } + if (!mIsStylusDown) { + ALOGE("Action UP or MOVE received with no prior action DOWN for stylus %d.", mFd.get()); + return false; + } + if (uinputAction == UinputAction::RELEASE && !handleStylusUp(tool, eventTime)) { + return false; + } + if (!writeInputEvent(EV_ABS, ABS_X, locationX, eventTime)) { + ALOGE("Unsupported x-axis location passed for stylus: %d.", locationX); + return false; + } + if (!writeInputEvent(EV_ABS, ABS_Y, locationY, eventTime)) { + ALOGE("Unsupported y-axis location passed for stylus: %d.", locationY); + return false; + } + if (!writeInputEvent(EV_ABS, ABS_TILT_X, tiltX, eventTime)) { + ALOGE("Unsupported x-axis tilt passed for stylus: %d.", tiltX); + return false; + } + if (!writeInputEvent(EV_ABS, ABS_TILT_Y, tiltY, eventTime)) { + ALOGE("Unsupported y-axis tilt passed for stylus: %d.", tiltY); + return false; + } + if (!writeInputEvent(EV_ABS, ABS_PRESSURE, pressure, eventTime)) { + ALOGE("Unsupported pressure passed for stylus: %d.", pressure); + return false; + } + if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) { + ALOGE("Failed to write SYN_REPORT for stylus motion event."); + return false; + } + return true; +} + +bool VirtualStylus::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction, + std::chrono::nanoseconds eventTime) { + return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING, + VirtualMouse::BUTTON_ACTION_MAPPING, eventTime); +} + +bool VirtualStylus::handleStylusDown(uint16_t tool, std::chrono::nanoseconds eventTime) { + if (mIsStylusDown) { + ALOGE("Repetitive action DOWN event received for a stylus that is already down."); + return false; + } + if (!writeInputEvent(EV_KEY, tool, static_cast<int32_t>(UinputAction::PRESS), eventTime)) { + ALOGE("Failed to write EV_KEY for stylus tool type: %u.", tool); + return false; + } + if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS), eventTime)) { + ALOGE("Failed to write BTN_TOUCH for stylus press."); + return false; + } + mIsStylusDown = true; + return true; +} + +bool VirtualStylus::handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime) { + if (!writeInputEvent(EV_KEY, tool, static_cast<int32_t>(UinputAction::RELEASE), eventTime)) { + ALOGE("Failed to write EV_KEY for stylus tool type: %u.", tool); + return false; + } + if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE), + eventTime)) { + ALOGE("Failed to write BTN_TOUCH for stylus release."); + return false; + } + mIsStylusDown = false; + return true; +} + } // namespace android diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl index 00ebd4d34f..c1aacfbc53 100644 --- a/libs/input/android/os/IInputFlinger.aidl +++ b/libs/input/android/os/IInputFlinger.aidl @@ -16,14 +16,13 @@ package android.os; -import android.InputChannel; +import android.os.InputChannelCore; import android.gui.FocusRequest; -import android.gui.WindowInfo; /** @hide */ interface IInputFlinger { - InputChannel createInputChannel(in @utf8InCpp String name); + InputChannelCore createInputChannel(in @utf8InCpp String name); void removeInputChannel(in IBinder connectionToken); /** * Sets focus to the window identified by the token. This must be called diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/os/InputChannelCore.aidl index c2d1112dd3..888a553268 100644 --- a/libs/input/android/InputChannel.aidl +++ b/libs/input/android/os/InputChannelCore.aidl @@ -15,6 +15,16 @@ ** limitations under the License. */ -package android; +package android.os; -parcelable InputChannel cpp_header "input/InputTransport.h"; +import android.os.ParcelFileDescriptor; + +/** + * Input channel struct for sending InputChannel between processes. + * @hide + */ +parcelable InputChannelCore { + @utf8InCpp String name; + ParcelFileDescriptor fd; + IBinder token; +} diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl index 4e644fff06..5d391551c2 100644 --- a/libs/input/android/os/InputConfig.aidl +++ b/libs/input/android/os/InputConfig.aidl @@ -150,4 +150,11 @@ enum InputConfig { * likely a duplicate window with the same client token, but different bounds. */ CLONE = 1 << 16, + + /** + * If the stylus is currently down *anywhere* on the screen, new touches will not be delivered + * to the window with this flag. This helps prevent unexpected clicks on some system windows, + * like StatusBar and TaskBar. + */ + GLOBAL_STYLUS_BLOCKS_TOUCH = 1 << 17, } diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index dbc002ccb3..bdec5c33cd 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -27,6 +27,13 @@ flag { namespace: "input" description: "Set to true to enable timer support for the touchpad Gestures library" bug: "297192727" + } + + flag { + name: "enable_input_event_tracing" + namespace: "input" + description: "Set to true to enable input event tracing, including always-on tracing on non-user builds" + bug: "210460522" } flag { @@ -53,15 +60,15 @@ flag { flag { name: "enable_touchpad_typing_palm_rejection" namespace: "input" - description: "Enable additional palm rejection on touchpad while typing" + description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard" bug: "301055381" } flag { - name: "remove_app_switch_drops" + name: "enable_v2_touchpad_typing_palm_rejection" namespace: "input" - description: "Remove the logic of dropping events due to pending app switch" - bug: "284808102" + description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click." + bug: "301055381" } flag { @@ -91,3 +98,31 @@ flag { description: "Remove pointer event tracking in WM after the Pointer Icon Refactor" bug: "315321016" } + +flag { + name: "enable_new_mouse_pointer_ballistics" + namespace: "input" + description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones" + bug: "315313622" +} + +flag { + name: "rate_limit_user_activity_poke_in_dispatcher" + namespace: "input" + description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher." + bug: "320499729" +} + +flag { + name: "input_device_view_behavior_api" + namespace: "input" + description: "Controls the API to provide InputDevice view behavior." + bug: "246946631" +} + +flag { + name: "enable_touchpad_fling_stop" + namespace: "input" + description: "Enable fling scrolling to be stopped by putting a finger on the touchpad again" + bug: "281106755" +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 9137a3468f..93af4c2066 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -26,6 +26,7 @@ cc_test { "TfLiteMotionPredictor_test.cpp", "TouchResampling_test.cpp", "TouchVideoFrame_test.cpp", + "VelocityControl_test.cpp", "VelocityTracker_test.cpp", "VerifiedInputEvent_test.cpp", ], diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 0661261003..60feb53dcc 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -32,37 +32,31 @@ namespace android { +namespace { +bool operator==(const InputChannel& left, const InputChannel& right) { + struct stat lhs, rhs; + if (fstat(left.getFd(), &lhs) != 0) { + return false; + } + if (fstat(right.getFd(), &rhs) != 0) { + return false; + } + // If file descriptors are pointing to same inode they are duplicated fds. + return left.getName() == right.getName() && + left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino; +} +} // namespace + class InputChannelTest : public testing::Test { }; +TEST_F(InputChannelTest, ClientAndServerTokensMatch) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; -TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { - // Our purpose here is to verify that the input channel destructor closes the - // file descriptor provided to it. One easy way is to provide it with one end - // of a pipe and to check for EPIPE on the other end after the channel is destroyed. - Pipe pipe; - - android::base::unique_fd sendFd(pipe.sendFd); - - std::unique_ptr<InputChannel> inputChannel = - InputChannel::create("channel name", std::move(sendFd), new BBinder()); - - EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; - EXPECT_STREQ("channel name", inputChannel->getName().c_str()) - << "channel should have provided name"; - EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd"; - - // InputChannel should be the owner of the file descriptor now - ASSERT_FALSE(sendFd.ok()); -} - -TEST_F(InputChannelTest, SetAndGetToken) { - Pipe pipe; - sp<IBinder> token = new BBinder(); - std::unique_ptr<InputChannel> channel = - InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token); - - EXPECT_EQ(token, channel->getConnectionToken()); + status_t result = + InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + EXPECT_EQ(serverChannel->getConnectionToken(), clientChannel->getConnectionToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { @@ -71,8 +65,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); - ASSERT_EQ(OK, result) - << "should have successfully opened a channel pair"; + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; // Name EXPECT_STREQ("channel name (server)", serverChannel->getName().c_str()) @@ -81,8 +74,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "client channel should have suffixed name"; // Server->Client communication - InputMessage serverMsg; - memset(&serverMsg, 0, sizeof(InputMessage)); + InputMessage serverMsg = {}; serverMsg.header.type = InputMessage::Type::KEY; serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) @@ -97,8 +89,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "client channel should receive the correct message from server channel"; // Client->Server communication - InputMessage clientReply; - memset(&clientReply, 0, sizeof(InputMessage)); + InputMessage clientReply = {}; clientReply.header.type = InputMessage::Type::FINISHED; clientReply.header.seq = 0x11223344; clientReply.body.finished.handled = true; @@ -116,6 +107,48 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "server channel should receive the correct message from client channel"; } +TEST_F(InputChannelTest, ProbablyHasInput) { + std::unique_ptr<InputChannel> senderChannel, receiverChannel; + + // Open a pair of channels. + status_t result = + InputChannel::openInputChannelPair("channel name", senderChannel, receiverChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + ASSERT_FALSE(receiverChannel->probablyHasInput()); + + // Send one message. + InputMessage serverMsg = {}; + serverMsg.header.type = InputMessage::Type::KEY; + serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; + EXPECT_EQ(OK, senderChannel->sendMessage(&serverMsg)) + << "server channel should be able to send message to client channel"; + + // Verify input is available. + bool hasInput = false; + do { + // The probablyHasInput() can return false positive under rare circumstances uncontrollable + // by the tests. Re-request the availability in this case. Returning |false| for a long + // time is not intended, and would cause a test timeout. + hasInput = receiverChannel->probablyHasInput(); + } while (!hasInput); + EXPECT_TRUE(hasInput) + << "client channel should observe that message is available before receiving it"; + + // Receive (consume) the message. + InputMessage clientMsg; + EXPECT_EQ(OK, receiverChannel->receiveMessage(&clientMsg)) + << "client channel should be able to receive message from server channel"; + EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) + << "client channel should receive the correct message from server channel"; + EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) + << "client channel should receive the correct message from server channel"; + + // Verify input is not available. + EXPECT_FALSE(receiverChannel->probablyHasInput()) + << "client should not observe any more messages after receiving the single one"; +} + TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { std::unique_ptr<InputChannel> serverChannel, clientChannel; @@ -195,25 +228,6 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { } } -TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) { - std::unique_ptr<InputChannel> serverChannel, clientChannel; - - status_t result = - InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel); - - ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - - InputChannel chan; - Parcel parcel; - ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel)); - parcel.setDataPosition(0); - chan.readFromParcel(&parcel); - - EXPECT_EQ(chan == *serverChannel, true) - << "inputchannel should be equal after parceling and unparceling.\n" - << "name " << chan.getName() << " name " << serverChannel->getName(); -} - TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) { std::unique_ptr<InputChannel> serverChannel, clientChannel; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 06b841be0d..35430207f9 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -40,11 +40,186 @@ struct Pointer { bool isResampled = false; }; +// A collection of arguments to be sent as publishMotionEvent(). The saved members of this struct +// allow to check the expectations against the event acquired from the InputReceiver. To help +// simplify expectation checking it carries members not present in MotionEvent, like |rawXScale|. +struct PublishMotionArgs { + const int32_t action; + const nsecs_t downTime; + const uint32_t seq; + const int32_t eventId; + const int32_t deviceId = 1; + const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; + const int32_t displayId = ADISPLAY_ID_DEFAULT; + const int32_t actionButton = 0; + const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; + const MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; + const float xScale = 2; + const float yScale = 3; + const float xOffset = -10; + const float yOffset = -20; + const float rawXScale = 4; + const float rawYScale = -5; + const float rawXOffset = -11; + const float rawYOffset = 42; + const float xPrecision = 0.25; + const float yPrecision = 0.5; + const float xCursorPosition = 1.3; + const float yCursorPosition = 50.6; + std::array<uint8_t, 32> hmac; + int32_t flags; + ui::Transform transform; + ui::Transform rawTransform; + const nsecs_t eventTime; + size_t pointerCount; + std::vector<PointerProperties> pointerProperties; + std::vector<PointerCoords> pointerCoords; + + PublishMotionArgs(int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers, + const uint32_t seq); +}; + +PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime, + const std::vector<Pointer>& pointers, const uint32_t inSeq) + : action(inAction), + downTime(inDownTime), + seq(inSeq), + eventId(InputEvent::nextId()), + eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) { + hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + if (action == AMOTION_EVENT_ACTION_CANCEL) { + flags |= AMOTION_EVENT_FLAG_CANCELED; + } + pointerCount = pointers.size(); + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties.push_back({}); + pointerProperties[i].clear(); + pointerProperties[i].id = pointers[i].id; + pointerProperties[i].toolType = ToolType::FINGER; + + pointerCoords.push_back({}); + pointerCoords[i].clear(); + pointerCoords[i].isResampled = pointers[i].isResampled; + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); + } + transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); + rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1}); +} + +// Checks expectations against |motionEvent| acquired from an InputConsumer. Floating point +// comparisons limit precision to EPSILON. +void verifyArgsEqualToEvent(const PublishMotionArgs& args, const MotionEvent& motionEvent) { + EXPECT_EQ(args.eventId, motionEvent.getId()); + EXPECT_EQ(args.deviceId, motionEvent.getDeviceId()); + EXPECT_EQ(args.source, motionEvent.getSource()); + EXPECT_EQ(args.displayId, motionEvent.getDisplayId()); + EXPECT_EQ(args.hmac, motionEvent.getHmac()); + EXPECT_EQ(args.action, motionEvent.getAction()); + EXPECT_EQ(args.downTime, motionEvent.getDownTime()); + EXPECT_EQ(args.flags, motionEvent.getFlags()); + EXPECT_EQ(args.edgeFlags, motionEvent.getEdgeFlags()); + EXPECT_EQ(args.metaState, motionEvent.getMetaState()); + EXPECT_EQ(args.buttonState, motionEvent.getButtonState()); + EXPECT_EQ(args.classification, motionEvent.getClassification()); + EXPECT_EQ(args.transform, motionEvent.getTransform()); + EXPECT_EQ(args.xOffset, motionEvent.getXOffset()); + EXPECT_EQ(args.yOffset, motionEvent.getYOffset()); + EXPECT_EQ(args.xPrecision, motionEvent.getXPrecision()); + EXPECT_EQ(args.yPrecision, motionEvent.getYPrecision()); + EXPECT_NEAR(args.xCursorPosition, motionEvent.getRawXCursorPosition(), EPSILON); + EXPECT_NEAR(args.yCursorPosition, motionEvent.getRawYCursorPosition(), EPSILON); + EXPECT_NEAR(args.xCursorPosition * args.xScale + args.xOffset, motionEvent.getXCursorPosition(), + EPSILON); + EXPECT_NEAR(args.yCursorPosition * args.yScale + args.yOffset, motionEvent.getYCursorPosition(), + EPSILON); + EXPECT_EQ(args.rawTransform, motionEvent.getRawTransform()); + EXPECT_EQ(args.eventTime, motionEvent.getEventTime()); + EXPECT_EQ(args.pointerCount, motionEvent.getPointerCount()); + EXPECT_EQ(0U, motionEvent.getHistorySize()); + + for (size_t i = 0; i < args.pointerCount; i++) { + SCOPED_TRACE(i); + EXPECT_EQ(args.pointerProperties[i].id, motionEvent.getPointerId(i)); + EXPECT_EQ(args.pointerProperties[i].toolType, motionEvent.getToolType(i)); + + const auto& pc = args.pointerCoords[i]; + EXPECT_EQ(pc, motionEvent.getSamplePointerCoords()[i]); + + EXPECT_NEAR(pc.getX() * args.rawXScale + args.rawXOffset, motionEvent.getRawX(i), EPSILON); + EXPECT_NEAR(pc.getY() * args.rawYScale + args.rawYOffset, motionEvent.getRawY(i), EPSILON); + EXPECT_NEAR(pc.getX() * args.xScale + args.xOffset, motionEvent.getX(i), EPSILON); + EXPECT_NEAR(pc.getY() * args.yScale + args.yOffset, motionEvent.getY(i), EPSILON); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent.getPressure(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent.getSize(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent.getTouchMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent.getTouchMinor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent.getToolMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent.getToolMinor(i)); + + // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is + // "up", and the positive y direction is "down". + const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + const float x = sinf(unscaledOrientation) * args.xScale; + const float y = -cosf(unscaledOrientation) * args.yScale; + EXPECT_EQ(atan2f(x, -y), motionEvent.getOrientation(i)); + } +} + +void publishMotionEvent(InputPublisher& publisher, const PublishMotionArgs& a) { + status_t status = + publisher.publishMotionEvent(a.seq, a.eventId, a.deviceId, a.source, a.displayId, + a.hmac, a.action, a.actionButton, a.flags, a.edgeFlags, + a.metaState, a.buttonState, a.classification, a.transform, + a.xPrecision, a.yPrecision, a.xCursorPosition, + a.yCursorPosition, a.rawTransform, a.downTime, a.eventTime, + a.pointerCount, a.pointerProperties.data(), + a.pointerCoords.data()); + ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; +} + +void sendAndVerifyFinishedSignal(InputConsumer& consumer, InputPublisher& publisher, uint32_t seq, + nsecs_t publishTime) { + status_t status = consumer.sendFinishedSignal(seq, false); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + Result<InputPublisher::ConsumerResponse> result = publisher.receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_FALSE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; +} + +void waitUntilInputAvailable(const InputConsumer& inputConsumer) { + bool hasInput; + do { + // The probablyHasInput() can return false positive under rare circumstances uncontrollable + // by the tests. Re-request the availability in this case. Returning |false| for a long + // time is not intended, and would cause a test timeout. + hasInput = inputConsumer.probablyHasInput(); + } while (!hasInput); +} + } // namespace class InputPublisherAndConsumerTest : public testing::Test { protected: - std::shared_ptr<InputChannel> mServerChannel, mClientChannel; std::unique_ptr<InputPublisher> mPublisher; std::unique_ptr<InputConsumer> mConsumer; PreallocatedInputEventFactory mEventFactory; @@ -54,15 +229,15 @@ protected: status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result); - mServerChannel = std::move(serverChannel); - mClientChannel = std::move(clientChannel); - mPublisher = std::make_unique<InputPublisher>(mServerChannel); - mConsumer = std::make_unique<InputConsumer>(mClientChannel); + mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel)); + mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel)); } void publishAndConsumeKeyEvent(); void publishAndConsumeMotionStream(); + void publishAndConsumeMotionDown(nsecs_t downTime); + void publishAndConsumeBatchedMotionMove(nsecs_t downTime); void publishAndConsumeFocusEvent(); void publishAndConsumeCaptureEvent(); void publishAndConsumeDragEvent(); @@ -73,24 +248,10 @@ protected: private: // The sequence number to use when publishing the next event uint32_t mSeq = 1; - - void publishAndConsumeMotionEvent( - int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, - int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - float xScale, float yScale, float xOffset, float yOffset, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, float rawXScale, - float rawYScale, float rawXOffset, float rawYOffset, nsecs_t downTime, - nsecs_t eventTime, const std::vector<PointerProperties>& pointerProperties, - const std::vector<PointerCoords>& pointerCoords); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { - ASSERT_NE(nullptr, mPublisher->getChannel()); - ASSERT_NE(nullptr, mConsumer->getChannel()); - EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get()); - EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get()); - ASSERT_EQ(mPublisher->getChannel()->getConnectionToken(), + ASSERT_EQ(mPublisher->getChannel().getConnectionToken(), mConsumer->getChannel()->getConnectionToken()); } @@ -121,11 +282,14 @@ void InputPublisherAndConsumerTest::publishAndConsumeKeyEvent() { ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + waitUntilInputAvailable(*mConsumer); uint32_t consumeSeq; InputEvent* event; status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; + EXPECT_FALSE(mConsumer->probablyHasInput()) + << "no events should be waiting after being consumed"; ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; @@ -185,176 +349,51 @@ void InputPublisherAndConsumerTest::publishAndConsumeMotionStream() { Pointer{.id = 2, .x = 300, .y = 400}}); } -void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent( - int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) { - constexpr int32_t deviceId = 1; - constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; - constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; - constexpr std::array<uint8_t, 32> hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; - constexpr int32_t actionButton = 0; - int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - - if (action == AMOTION_EVENT_ACTION_CANCEL) { - flags |= AMOTION_EVENT_FLAG_CANCELED; - } - const size_t pointerCount = pointers.size(); - constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; - constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; - constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; - constexpr float xScale = 2; - constexpr float yScale = 3; - constexpr float xOffset = -10; - constexpr float yOffset = -20; - constexpr float rawXScale = 4; - constexpr float rawYScale = -5; - constexpr float rawXOffset = -11; - constexpr float rawYOffset = 42; - constexpr float xPrecision = 0.25; - constexpr float yPrecision = 0.5; - constexpr float xCursorPosition = 1.3; - constexpr float yCursorPosition = 50.6; - - const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC); - std::vector<PointerProperties> pointerProperties; - std::vector<PointerCoords> pointerCoords; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties.push_back({}); - pointerProperties[i].clear(); - pointerProperties[i].id = pointers[i].id; - pointerProperties[i].toolType = ToolType::FINGER; +void InputPublisherAndConsumerTest::publishAndConsumeMotionDown(nsecs_t downTime) { + publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}}); +} - pointerCoords.push_back({}); - pointerCoords[i].clear(); - pointerCoords[i].isResampled = pointers[i].isResampled; - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); - } +void InputPublisherAndConsumerTest::publishAndConsumeBatchedMotionMove(nsecs_t downTime) { + uint32_t seq = mSeq++; + const std::vector<Pointer> pointers = {Pointer{.id = 0, .x = 20, .y = 30}}; + PublishMotionArgs args(AMOTION_EVENT_ACTION_MOVE, downTime, pointers, seq); + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + publishMotionEvent(*mPublisher, args); - publishAndConsumeMotionEvent(deviceId, source, displayId, hmac, action, actionButton, flags, - edgeFlags, metaState, buttonState, classification, xScale, yScale, - xOffset, yOffset, xPrecision, yPrecision, xCursorPosition, - yCursorPosition, rawXScale, rawYScale, rawXOffset, rawYOffset, - downTime, eventTime, pointerProperties, pointerCoords); + // Consume leaving a batch behind. + uint32_t consumeSeq; + InputEvent* event; + status_t status = mConsumer->consume(&mEventFactory, + /*consumeBatches=*/false, -1, &consumeSeq, &event); + ASSERT_EQ(WOULD_BLOCK, status) + << "consumer consume should return WOULD_BLOCK when a new batch is started"; + ASSERT_TRUE(mConsumer->hasPendingBatch()) << "consume should have created a batch"; + EXPECT_TRUE(mConsumer->probablyHasInput()) + << "should deterministically have input because there is a batch"; + sendAndVerifyFinishedSignal(*mConsumer, *mPublisher, seq, publishTime); } void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent( - int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, - int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xScale, float yScale, - float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, float rawXScale, float rawYScale, float rawXOffset, float rawYOffset, - nsecs_t downTime, nsecs_t eventTime, - const std::vector<PointerProperties>& pointerProperties, - const std::vector<PointerCoords>& pointerCoords) { - const uint32_t seq = mSeq++; - const int32_t eventId = InputEvent::nextId(); - ui::Transform transform; - transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); - ui::Transform rawTransform; - rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1}); - - status_t status; - ASSERT_EQ(pointerProperties.size(), pointerCoords.size()); - const size_t pointerCount = pointerProperties.size(); - const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); - status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, - actionButton, flags, edgeFlags, metaState, buttonState, - classification, transform, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, rawTransform, - downTime, eventTime, pointerCount, - pointerProperties.data(), pointerCoords.data()); - ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; + int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) { + uint32_t seq = mSeq++; + PublishMotionArgs args(action, downTime, pointers, seq); + nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + publishMotionEvent(*mPublisher, args); uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); - ASSERT_EQ(OK, status) - << "consumer consume should return OK"; - + status_t status = + mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(InputEventType::MOTION, event->getType()) << "consumer should have returned a motion event"; - - MotionEvent* motionEvent = static_cast<MotionEvent*>(event); EXPECT_EQ(seq, consumeSeq); - EXPECT_EQ(eventId, motionEvent->getId()); - EXPECT_EQ(deviceId, motionEvent->getDeviceId()); - EXPECT_EQ(source, motionEvent->getSource()); - EXPECT_EQ(displayId, motionEvent->getDisplayId()); - EXPECT_EQ(hmac, motionEvent->getHmac()); - EXPECT_EQ(action, motionEvent->getAction()); - EXPECT_EQ(flags, motionEvent->getFlags()); - EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); - EXPECT_EQ(metaState, motionEvent->getMetaState()); - EXPECT_EQ(buttonState, motionEvent->getButtonState()); - EXPECT_EQ(classification, motionEvent->getClassification()); - EXPECT_EQ(transform, motionEvent->getTransform()); - EXPECT_EQ(xOffset, motionEvent->getXOffset()); - EXPECT_EQ(yOffset, motionEvent->getYOffset()); - EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); - EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); - EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON); - EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON); - EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON); - EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON); - EXPECT_EQ(rawTransform, motionEvent->getRawTransform()); - EXPECT_EQ(downTime, motionEvent->getDownTime()); - EXPECT_EQ(eventTime, motionEvent->getEventTime()); - EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); - EXPECT_EQ(0U, motionEvent->getHistorySize()); - for (size_t i = 0; i < pointerCount; i++) { - SCOPED_TRACE(i); - EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); - EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); - - const auto& pc = pointerCoords[i]; - EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]); - - EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON); - EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON); - EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON); - EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i)); - - // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is - // "up", and the positive y direction is "down". - const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - const float x = sinf(unscaledOrientation) * xScale; - const float y = -cosf(unscaledOrientation) * yScale; - EXPECT_EQ(atan2f(x, -y), motionEvent->getOrientation(i)); - } - - status = mConsumer->sendFinishedSignal(seq, false); - ASSERT_EQ(OK, status) - << "consumer sendFinishedSignal should return OK"; - - Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); - ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; - ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); - const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); - ASSERT_EQ(seq, finish.seq) - << "receiveConsumerResponse should have returned the original sequence number"; - ASSERT_FALSE(finish.handled) - << "receiveConsumerResponse should have set handled to consumer's reply"; - ASSERT_GE(finish.consumeTime, publishTime) - << "finished signal's consume time should be greater than publish time"; + verifyArgsEqualToEvent(args, static_cast<const MotionEvent&>(*event)); + sendAndVerifyFinishedSignal(*mConsumer, *mPublisher, seq, publishTime); } void InputPublisherAndConsumerTest::publishAndConsumeFocusEvent() { @@ -546,6 +585,15 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionStream()); } +TEST_F(InputPublisherAndConsumerTest, PublishMotionMoveEvent_EndToEnd) { + // Publish a DOWN event before MOVE to pass the InputVerifier checks. + const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionDown(downTime)); + + // Publish the MOVE event and check expectations. + ASSERT_NO_FATAL_FAILURE(publishAndConsumeBatchedMotionMove(downTime)); +} + TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent()); } diff --git a/libs/input/tests/VelocityControl_test.cpp b/libs/input/tests/VelocityControl_test.cpp new file mode 100644 index 0000000000..63d64c6048 --- /dev/null +++ b/libs/input/tests/VelocityControl_test.cpp @@ -0,0 +1,129 @@ +/* + * 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 <input/VelocityControl.h> + +#include <limits> + +#include <gtest/gtest.h> +#include <input/AccelerationCurve.h> +#include <utils/Timers.h> + +namespace android { + +namespace { + +constexpr float EPSILON = 0.001; +constexpr float COUNTS_PER_MM = 800 / 25.4; + +} // namespace + +class CurvedVelocityControlTest : public testing::Test { +protected: + CurvedVelocityControl mCtrl; + + void moveWithoutCheckingResult(nsecs_t eventTime, float deltaX, float deltaY) { + mCtrl.move(eventTime, &deltaX, &deltaY); + } + + void moveAndCheckRatio(nsecs_t eventTime, const float deltaX, const float deltaY, + float expectedRatio) { + float newDeltaX = deltaX, newDeltaY = deltaY; + mCtrl.move(eventTime, &newDeltaX, &newDeltaY); + ASSERT_NEAR(expectedRatio * deltaX, newDeltaX, EPSILON) + << "Expected ratio of " << expectedRatio << " in X, but actual ratio was " + << newDeltaX / deltaX; + ASSERT_NEAR(expectedRatio * deltaY, newDeltaY, EPSILON) + << "Expected ratio of " << expectedRatio << " in Y, but actual ratio was " + << newDeltaY / deltaY; + } +}; + +TEST_F(CurvedVelocityControlTest, SegmentSelection) { + // To make the maths simple, use a "curve" that's actually just a sequence of steps. + mCtrl.setCurve({ + {10, 2, 0}, + {20, 3, 0}, + {30, 4, 0}, + {std::numeric_limits<double>::infinity(), 5, 0}, + }); + + // Establish a velocity of 16 mm/s. + moveWithoutCheckingResult(0, 0, 0); + moveWithoutCheckingResult(10'000'000, 0.16 * COUNTS_PER_MM, 0); + moveWithoutCheckingResult(20'000'000, 0.16 * COUNTS_PER_MM, 0); + moveWithoutCheckingResult(30'000'000, 0.16 * COUNTS_PER_MM, 0); + ASSERT_NO_FATAL_FAILURE( + moveAndCheckRatio(40'000'000, 0.16 * COUNTS_PER_MM, 0, /*expectedRatio=*/3)); + + // Establish a velocity of 50 mm/s. + mCtrl.reset(); + moveWithoutCheckingResult(100'000'000, 0, 0); + moveWithoutCheckingResult(110'000'000, 0.50 * COUNTS_PER_MM, 0); + moveWithoutCheckingResult(120'000'000, 0.50 * COUNTS_PER_MM, 0); + moveWithoutCheckingResult(130'000'000, 0.50 * COUNTS_PER_MM, 0); + ASSERT_NO_FATAL_FAILURE( + moveAndCheckRatio(140'000'000, 0.50 * COUNTS_PER_MM, 0, /*expectedRatio=*/5)); +} + +TEST_F(CurvedVelocityControlTest, RatioDefaultsToFirstSegmentWhenVelocityIsUnknown) { + mCtrl.setCurve({ + {10, 3, 0}, + {20, 2, 0}, + {std::numeric_limits<double>::infinity(), 4, 0}, + }); + + // Only send two moves, which won't be enough for VelocityTracker to calculate a velocity from. + moveWithoutCheckingResult(0, 0, 0); + ASSERT_NO_FATAL_FAILURE( + moveAndCheckRatio(10'000'000, 0.25 * COUNTS_PER_MM, 0, /*expectedRatio=*/3)); +} + +TEST_F(CurvedVelocityControlTest, VelocityCalculatedUsingBothAxes) { + mCtrl.setCurve({ + {8.0, 3, 0}, + {8.1, 2, 0}, + {std::numeric_limits<double>::infinity(), 4, 0}, + }); + + // Establish a velocity of 8.06 (= √65 = √(7²+4²)) mm/s between the two axes. + moveWithoutCheckingResult(0, 0, 0); + moveWithoutCheckingResult(10'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM); + moveWithoutCheckingResult(20'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM); + moveWithoutCheckingResult(30'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM); + ASSERT_NO_FATAL_FAILURE(moveAndCheckRatio(40'000'000, 0.07 * COUNTS_PER_MM, + 0.04 * COUNTS_PER_MM, + /*expectedRatio=*/2)); +} + +TEST_F(CurvedVelocityControlTest, ReciprocalTerm) { + mCtrl.setCurve({ + {10, 2, 0}, + {20, 3, -10}, + {std::numeric_limits<double>::infinity(), 3, 0}, + }); + + // Establish a velocity of 15 mm/s. + moveWithoutCheckingResult(0, 0, 0); + moveWithoutCheckingResult(10'000'000, 0, 0.15 * COUNTS_PER_MM); + moveWithoutCheckingResult(20'000'000, 0, 0.15 * COUNTS_PER_MM); + moveWithoutCheckingResult(30'000'000, 0, 0.15 * COUNTS_PER_MM); + // Expected ratio is 3 - 10 / 15 = 2.33333... + ASSERT_NO_FATAL_FAILURE( + moveAndCheckRatio(40'000'000, 0, 0.15 * COUNTS_PER_MM, /*expectedRatio=*/2.33333)); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index 342f5de337..03f4f399d0 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -16,6 +16,7 @@ package { default_applicable_licenses: [ "frameworks_native_libs_nativedisplay_license", ], + default_team: "trendy_team_android_core_graphics_stack", } // Added automatically by a large-scale-change @@ -33,7 +34,13 @@ license { cc_library_headers { name: "libnativedisplay_headers", + host_supported: true, export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + }, + }, } cc_library_shared { diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index e7b2195056..52612870ab 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -113,6 +113,22 @@ static_assert( AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM, "HAL and AHardwareBuffer pixel format don't match"); +static enum AHardwareBufferStatus filterStatus(status_t status) { + switch (status) { + case STATUS_OK: + return AHARDWAREBUFFER_STATUS_OK; + case STATUS_NO_MEMORY: + return AHARDWAREBUFFER_STATUS_NO_MEMORY; + case STATUS_BAD_VALUE: + return AHARDWAREBUFFER_STATUS_BAD_VALUE; + case STATUS_UNKNOWN_TRANSACTION: + case STATUS_INVALID_OPERATION: + return AHARDWAREBUFFER_STATUS_UNSUPPORTED; + default: + return AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR; + } +} + // ---------------------------------------------------------------------------- // Public functions // ---------------------------------------------------------------------------- @@ -511,6 +527,24 @@ binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull bu return AParcel_viewPlatformParcel(parcel)->write(*gb); } +ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) { + const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + if (!gb) return ADATASPACE_UNKNOWN; + ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; + status_t status = gb->getDataspace(&dataspace); + if (status != OK) { + return ADATASPACE_UNKNOWN; + } + return static_cast<ADataSpace>(dataspace); +} + +enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* buffer, + ADataSpace dataspace) { + GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + auto& mapper = GraphicBufferMapper::get(); + return filterStatus(mapper.setDataspace(gb->handle, static_cast<ui::Dataspace>(dataspace))); +} + // ---------------------------------------------------------------------------- // VNDK functions // ---------------------------------------------------------------------------- @@ -552,6 +586,56 @@ int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, return NO_ERROR; } +enum AHardwareBufferStatus AHardwareBuffer_allocateWithOptions( + const AHardwareBuffer_Desc* desc, const AHardwareBufferLongOptions* additionalOptions, + size_t additionalOptionsSize, AHardwareBuffer** outBuffer) { + (void)additionalOptions; + (void)additionalOptionsSize; + if (!outBuffer || !desc) return AHARDWAREBUFFER_STATUS_BAD_VALUE; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) { + return AHARDWAREBUFFER_STATUS_BAD_VALUE; + } + + int format = AHardwareBuffer_convertToPixelFormat(desc->format); + uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); + + std::vector<GraphicBufferAllocator::AdditionalOptions> extras; + extras.reserve(additionalOptionsSize); + for (size_t i = 0; i < additionalOptionsSize; i++) { + extras.push_back(GraphicBufferAllocator::AdditionalOptions{additionalOptions[i].name, + additionalOptions[i].value}); + } + + const auto extrasCount = extras.size(); + auto gbuffer = sp<GraphicBuffer>::make(GraphicBufferAllocator::AllocationRequest{ + .importBuffer = true, + .width = desc->width, + .height = desc->height, + .format = format, + .layerCount = desc->layers, + .usage = usage, + .requestorName = std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]", + .extras = std::move(extras), + }); + + status_t err = gbuffer->initCheck(); + if (err != 0 || gbuffer->handle == nullptr) { + if (err == NO_MEMORY) { + GraphicBuffer::dumpAllocationsToSystemLog(); + } + ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u, extrasCount=%zd) failed (%s), handle=%p", + desc->width, desc->height, desc->layers, extrasCount, strerror(-err), + gbuffer->handle); + return filterStatus(err == 0 ? UNKNOWN_ERROR : err); + } + + *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); + + // Ensure the buffer doesn't get destroyed when the sp<> goes away. + AHardwareBuffer_acquire(*outBuffer); + return AHARDWAREBUFFER_STATUS_OK; +} + // ---------------------------------------------------------------------------- // Helpers implementation // ---------------------------------------------------------------------------- @@ -652,12 +736,9 @@ uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) { return ahardwarebuffer_format; } +// TODO: Remove, this is just to make an overly aggressive ABI checker happy int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) { - GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); - auto& mapper = GraphicBufferMapper::get(); - ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; - mapper.getDataspace(gb->handle, &dataspace); - return static_cast<int32_t>(dataspace); + return ::AHardwareBuffer_getDataSpace(buffer); } uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index dd5958de28..f97eed5db3 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -152,31 +152,56 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) { static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN)); - static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); - static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); - static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); - static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); - static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); - static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); - static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); - static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); - static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); - static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); - static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); - static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); - static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); - static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); - static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); - static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); - static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); - static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK)); - static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); - static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL)); - static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); - static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_MASK) == + static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT709) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625_UNADJUSTED) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525_UNADJUSTED) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT470M) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_FILM) == + static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_DCI_P3) == + static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_ADOBE_RGB) == + static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_MASK) == + static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_LINEAR) == + static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_SMPTE_170M) == + static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_2) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_6) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_8) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_ST2084) == + static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_HLG) == + static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); + static_assert(static_cast<int>(ADATASPACE_RANGE_MASK) == + static_cast<int>(HAL_DATASPACE_RANGE_MASK)); + static_assert(static_cast<int>(ADATASPACE_RANGE_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_RANGE_FULL) == + static_cast<int>(HAL_DATASPACE_RANGE_FULL)); + static_assert(static_cast<int>(ADATASPACE_RANGE_LIMITED) == + static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); + static_assert(static_cast<int>(ADATASPACE_RANGE_EXTENDED) == + static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB)); static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB)); static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index bc0bfc52d5..855807472e 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -16,6 +16,7 @@ package { default_applicable_licenses: [ "frameworks_native_libs_nativewindow_license", ], + default_team: "trendy_team_android_core_graphics_stack", } // Added automatically by a large-scale-change @@ -53,6 +54,11 @@ cc_library_headers { "test_com.android.media.swcodec", ], host_supported: true, + target: { + windows: { + enabled: true, + }, + }, } ndk_library { diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h index 880c694934..f145a2f7c2 100644 --- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h +++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h @@ -27,8 +27,8 @@ #include <stdint.h> -struct AHardwareBuffer; -struct AHardwareBuffer_Desc; +#include <vndk/hardware_buffer.h> + struct ANativeWindowBuffer; namespace android { @@ -46,11 +46,6 @@ uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format); // convert HAL format to AHardwareBuffer format (note: this is a no-op) uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format); -// retrieves a dataspace from the AHardwareBuffer metadata, if the device -// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not -// supported. -int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer); - // convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op) uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage); diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 9f8ae863db..8056d9ac4f 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -64,7 +64,7 @@ enum ADataSpace : int32_t { * Defines the chromaticity coordinates of the source primaries in terms of * the CIE 1931 definition of x and y specified in ISO 11664-1. */ - STANDARD_MASK = 63 << 16, + ADATASPACE_STANDARD_MASK = 63 << 16, /** * Chromacity coordinates are unknown or are determined by the application. @@ -79,7 +79,7 @@ enum ADataSpace : int32_t { * For all other formats standard is undefined, and implementations should use * an appropriate standard for the data represented. */ - STANDARD_UNSPECIFIED = 0 << 16, + ADATASPACE_STANDARD_UNSPECIFIED = 0 << 16, /** * <pre> @@ -92,7 +92,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation * for RGB conversion. */ - STANDARD_BT709 = 1 << 16, + ADATASPACE_STANDARD_BT709 = 1 << 16, /** * <pre> @@ -107,7 +107,7 @@ enum ADataSpace : int32_t { * to minimize the color shift into RGB space that uses BT.709 * primaries. */ - STANDARD_BT601_625 = 2 << 16, + ADATASPACE_STANDARD_BT601_625 = 2 << 16, /** * <pre> @@ -120,7 +120,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation * for RGB conversion. */ - STANDARD_BT601_625_UNADJUSTED = 3 << 16, + ADATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << 16, /** * <pre> @@ -135,7 +135,7 @@ enum ADataSpace : int32_t { * to minimize the color shift into RGB space that uses BT.709 * primaries. */ - STANDARD_BT601_525 = 4 << 16, + ADATASPACE_STANDARD_BT601_525 = 4 << 16, /** * <pre> @@ -148,7 +148,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation * for RGB conversion (as in SMPTE 240M). */ - STANDARD_BT601_525_UNADJUSTED = 5 << 16, + ADATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << 16, /** * <pre> @@ -161,7 +161,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation * for RGB conversion. */ - STANDARD_BT2020 = 6 << 16, + ADATASPACE_STANDARD_BT2020 = 6 << 16, /** * <pre> @@ -174,7 +174,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation * for RGB conversion using the linear domain. */ - STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, + ADATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, /** * <pre> @@ -187,7 +187,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation * for RGB conversion. */ - STANDARD_BT470M = 8 << 16, + ADATASPACE_STANDARD_BT470M = 8 << 16, /** * <pre> @@ -200,7 +200,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation * for RGB conversion. */ - STANDARD_FILM = 9 << 16, + ADATASPACE_STANDARD_FILM = 9 << 16, /** * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3) @@ -211,7 +211,7 @@ enum ADataSpace : int32_t { * red 0.680 0.320 * white (D65) 0.3127 0.3290</pre> */ - STANDARD_DCI_P3 = 10 << 16, + ADATASPACE_STANDARD_DCI_P3 = 10 << 16, /** * Adobe RGB @@ -222,7 +222,7 @@ enum ADataSpace : int32_t { * red 0.640 0.330 * white (D65) 0.3127 0.3290</pre> */ - STANDARD_ADOBE_RGB = 11 << 16, + ADATASPACE_STANDARD_ADOBE_RGB = 11 << 16, /** * Transfer aspect @@ -237,7 +237,7 @@ enum ADataSpace : int32_t { * component. Implementation may apply the transfer function in RGB space * for all pixel formats if desired. */ - TRANSFER_MASK = 31 << 22, + ADATASPACE_TRANSFER_MASK = 31 << 22, /** * Transfer characteristics are unknown or are determined by the @@ -245,13 +245,13 @@ enum ADataSpace : int32_t { * * Implementations should use the following transfer functions: * - * For YCbCr formats: use TRANSFER_SMPTE_170M - * For RGB formats: use TRANSFER_SRGB + * For YCbCr formats: use ADATASPACE_TRANSFER_SMPTE_170M + * For RGB formats: use ADATASPACE_TRANSFER_SRGB * * For all other formats transfer function is undefined, and implementations * should use an appropriate standard for the data represented. */ - TRANSFER_UNSPECIFIED = 0 << 22, + ADATASPACE_TRANSFER_UNSPECIFIED = 0 << 22, /** * Linear transfer. @@ -261,7 +261,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_LINEAR = 1 << 22, + ADATASPACE_TRANSFER_LINEAR = 1 << 22, /** * sRGB transfer. @@ -272,7 +272,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_SRGB = 2 << 22, + ADATASPACE_TRANSFER_SRGB = 2 << 22, /** * SMPTE 170M transfer. @@ -283,7 +283,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_SMPTE_170M = 3 << 22, + ADATASPACE_TRANSFER_SMPTE_170M = 3 << 22, /** * Display gamma 2.2. @@ -293,7 +293,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_2 = 4 << 22, + ADATASPACE_TRANSFER_GAMMA2_2 = 4 << 22, /** * Display gamma 2.6. @@ -303,7 +303,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_6 = 5 << 22, + ADATASPACE_TRANSFER_GAMMA2_6 = 5 << 22, /** * Display gamma 2.8. @@ -313,7 +313,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_8 = 6 << 22, + ADATASPACE_TRANSFER_GAMMA2_8 = 6 << 22, /** * SMPTE ST 2084 (Dolby Perceptual Quantizer). @@ -329,7 +329,7 @@ enum ADataSpace : int32_t { * L = 1 corresponds to 10000 cd/m2 * E - corresponding electrical signal</pre> */ - TRANSFER_ST2084 = 7 << 22, + ADATASPACE_TRANSFER_ST2084 = 7 << 22, /** * ARIB STD-B67 Hybrid Log Gamma. @@ -345,7 +345,7 @@ enum ADataSpace : int32_t { * to reference white level of 100 cd/m2 * E - corresponding electrical signal</pre> */ - TRANSFER_HLG = 8 << 22, + ADATASPACE_TRANSFER_HLG = 8 << 22, /** * Range aspect @@ -353,7 +353,7 @@ enum ADataSpace : int32_t { * Defines the range of values corresponding to the unit range of 0-1. * This is defined for YCbCr only, but can be expanded to RGB space. */ - RANGE_MASK = 7 << 27, + ADATASPACE_RANGE_MASK = 7 << 27, /** * Range is unknown or are determined by the application. Implementations @@ -366,13 +366,13 @@ enum ADataSpace : int32_t { * For all other formats range is undefined, and implementations should use * an appropriate range for the data represented. */ - RANGE_UNSPECIFIED = 0 << 27, + ADATASPACE_RANGE_UNSPECIFIED = 0 << 27, /** * Full range uses all values for Y, Cb and Cr from * 0 to 2^b-1, where b is the bit depth of the color format. */ - RANGE_FULL = 1 << 27, + ADATASPACE_RANGE_FULL = 1 << 27, /** * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and @@ -387,7 +387,7 @@ enum ADataSpace : int32_t { * Luma (Y) samples should range from 64 to 940, inclusive * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive */ - RANGE_LIMITED = 2 << 27, + ADATASPACE_RANGE_LIMITED = 2 << 27, /** * Extended range is used for scRGB. Intended for use with @@ -396,7 +396,7 @@ enum ADataSpace : int32_t { * color outside the sRGB gamut. * Used to blend / merge multiple dataspaces on a single display. */ - RANGE_EXTENDED = 3 << 27, + ADATASPACE_RANGE_EXTENDED = 3 << 27, /** * scRGB linear encoding @@ -411,7 +411,8 @@ enum ADataSpace : int32_t { * * Uses extended range, linear transfer and BT.709 standard. */ - ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED + ADATASPACE_SCRGB_LINEAR = 406913024, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR | + // ADATASPACE_RANGE_EXTENDED /** * sRGB gamma encoding @@ -426,7 +427,8 @@ enum ADataSpace : int32_t { * * Uses full range, sRGB transfer BT.709 standard. */ - ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL + ADATASPACE_SRGB = 142671872, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_FULL /** * scRGB @@ -441,14 +443,16 @@ enum ADataSpace : int32_t { * * Uses extended range, sRGB transfer and BT.709 standard. */ - ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED + ADATASPACE_SCRGB = 411107328, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_EXTENDED /** * Display P3 * * Uses full range, sRGB transfer and D65 DCI-P3 standard. */ - ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL + ADATASPACE_DISPLAY_P3 = 143261696, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) @@ -457,7 +461,8 @@ enum ADataSpace : int32_t { * * Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard. */ - ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL + ADATASPACE_BT2020_PQ = 163971072, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) @@ -466,7 +471,8 @@ enum ADataSpace : int32_t { * * Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard. */ - ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED + ADATASPACE_BT2020_ITU_PQ = 298188800, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 + // | ADATASPACE_RANGE_LIMITED /** * Adobe RGB @@ -476,7 +482,8 @@ enum ADataSpace : int32_t { * Note: Application is responsible for gamma encoding the data as * a 2.2 gamma encoding is not supported in HW. */ - ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL + ADATASPACE_ADOBE_RGB = 151715840, // ADATASPACE_STANDARD_ADOBE_RGB | + // ADATASPACE_TRANSFER_GAMMA2_2 | ADATASPACE_RANGE_FULL /** * JPEG File Interchange Format (JFIF) @@ -485,7 +492,8 @@ enum ADataSpace : int32_t { * * Uses full range, SMPTE 170M transfer and BT.601_625 standard. */ - ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL + ADATASPACE_JFIF = 146931712, // ADATASPACE_STANDARD_BT601_625 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 601 (BT.601) - 625-line @@ -494,7 +502,8 @@ enum ADataSpace : int32_t { * * Uses limited range, SMPTE 170M transfer and BT.601_625 standard. */ - ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT601_625 = 281149440, // ADATASPACE_STANDARD_BT601_625 | + // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED /** * ITU-R Recommendation 601 (BT.601) - 525-line @@ -503,7 +512,8 @@ enum ADataSpace : int32_t { * * Uses limited range, SMPTE 170M transfer and BT.601_525 standard. */ - ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT601_525 = 281280512, // ADATASPACE_STANDARD_BT601_525 | + // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED /** * ITU-R Recommendation 2020 (BT.2020) @@ -512,7 +522,8 @@ enum ADataSpace : int32_t { * * Uses full range, SMPTE 170M transfer and BT2020 standard. */ - ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL + ADATASPACE_BT2020 = 147193856, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 709 (BT.709) @@ -521,7 +532,8 @@ enum ADataSpace : int32_t { * * Uses limited range, SMPTE 170M transfer and BT.709 standard. */ - ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT709 = 281083904, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_LIMITED /** * SMPTE EG 432-1 and SMPTE RP 431-2 @@ -533,7 +545,8 @@ enum ADataSpace : int32_t { * Note: Application is responsible for gamma encoding the data as * a 2.6 gamma encoding is not supported in HW. */ - ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL + ADATASPACE_DCI_P3 = 155844608, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_GAMMA2_6 | + // ADATASPACE_RANGE_FULL /** * sRGB linear encoding @@ -547,21 +560,24 @@ enum ADataSpace : int32_t { * * Uses full range, linear transfer and BT.709 standard. */ - ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL + ADATASPACE_SRGB_LINEAR = 138477568, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR | + // ADATASPACE_RANGE_FULL /** * Hybrid Log Gamma encoding * * Uses full range, hybrid log gamma transfer and BT2020 standard. */ - ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL + ADATASPACE_BT2020_HLG = 168165376, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | + // ADATASPACE_RANGE_FULL /** * ITU Hybrid Log Gamma encoding * * Uses limited range, hybrid log gamma transfer and BT2020 standard. */ - ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED + ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | + // ADATASPACE_RANGE_LIMITED /** * Depth @@ -575,7 +591,37 @@ enum ADataSpace : int32_t { * * Embedded depth metadata following the dynamic depth specification. */ - ADATASPACE_DYNAMIC_DEPTH = 4098 + ADATASPACE_DYNAMIC_DEPTH = 4098, + +#ifndef ADATASPACE_SKIP_LEGACY_DEFINES + STANDARD_MASK = ADATASPACE_STANDARD_MASK, + STANDARD_UNSPECIFIED = ADATASPACE_STANDARD_UNSPECIFIED, + STANDARD_BT709 = ADATASPACE_STANDARD_BT709, + STANDARD_BT601_625 = ADATASPACE_STANDARD_BT601_625, + STANDARD_BT601_625_UNADJUSTED = ADATASPACE_STANDARD_BT601_625_UNADJUSTED, + STANDARD_BT601_525 = ADATASPACE_STANDARD_BT601_525, + STANDARD_BT601_525_UNADJUSTED = ADATASPACE_STANDARD_BT601_525_UNADJUSTED, + STANDARD_BT470M = ADATASPACE_STANDARD_BT470M, + STANDARD_BT2020 = ADATASPACE_STANDARD_BT2020, + STANDARD_FILM = ADATASPACE_STANDARD_FILM, + STANDARD_DCI_P3 = ADATASPACE_STANDARD_DCI_P3, + STANDARD_ADOBE_RGB = ADATASPACE_STANDARD_ADOBE_RGB, + TRANSFER_MASK = ADATASPACE_TRANSFER_MASK, + TRANSFER_UNSPECIFIED = ADATASPACE_TRANSFER_UNSPECIFIED, + TRANSFER_LINEAR = ADATASPACE_TRANSFER_LINEAR, + TRANSFER_SMPTE_170M = ADATASPACE_TRANSFER_SMPTE_170M, + TRANSFER_GAMMA2_2 = ADATASPACE_TRANSFER_GAMMA2_2, + TRANSFER_GAMMA2_6 = ADATASPACE_TRANSFER_GAMMA2_6, + TRANSFER_GAMMA2_8 = ADATASPACE_TRANSFER_GAMMA2_8, + TRANSFER_SRGB = ADATASPACE_TRANSFER_SRGB, + TRANSFER_ST2084 = ADATASPACE_TRANSFER_ST2084, + TRANSFER_HLG = ADATASPACE_TRANSFER_HLG, + RANGE_MASK = ADATASPACE_RANGE_MASK, + RANGE_UNSPECIFIED = ADATASPACE_RANGE_UNSPECIFIED, + RANGE_FULL = ADATASPACE_RANGE_FULL, + RANGE_LIMITED = ADATASPACE_RANGE_LIMITED, + RANGE_EXTENDED = ADATASPACE_RANGE_EXTENDED, +#endif }; __END_DECLS diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index 21798d0e29..6fcb3a4842 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -46,9 +46,16 @@ #define ANDROID_HARDWARE_BUFFER_H #include <android/rect.h> +#define ADATASPACE_SKIP_LEGACY_DEFINES +#include <android/data_space.h> +#undef ADATASPACE_SKIP_LEGACY_DEFINES #include <inttypes.h> #include <sys/cdefs.h> +#if !defined(__INTRODUCED_IN) +#define __INTRODUCED_IN(__api_level) /* nothing */ +#endif + __BEGIN_DECLS // clang-format off diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index a98ea86073..969a5cff05 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1095,10 +1095,19 @@ enum { ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL = 3, /** + * Indicates that, as a result of a user interaction, an animation is likely to start. + * This category is a signal that a user interaction heuristic determined the need of a + * high refresh rate, and is not an explicit request from the app. + * As opposed to FRAME_RATE_CATEGORY_HIGH, this vote may be ignored in favor of + * more explicit votes. + */ + ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH_HINT = 4, + + /** * Indicates a frame rate suitable for animations that require a high frame rate, which may * increase smoothness but may also increase power usage. */ - ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 4 + ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 5 }; /* diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h index 21931bb553..48fb02f69d 100644 --- a/libs/nativewindow/include/vndk/hardware_buffer.h +++ b/libs/nativewindow/include/vndk/hardware_buffer.h @@ -21,6 +21,7 @@ #include <android/hardware_buffer.h> #include <cutils/native_handle.h> +#include <errno.h> __BEGIN_DECLS @@ -105,6 +106,76 @@ enum { AHARDWAREBUFFER_USAGE_CAMERA_MASK = 6UL << 16, }; +/** + * Additional options for AHardwareBuffer_allocateWithOptions. These correspond to + * android.hardware.graphics.common.ExtendableType + */ +typedef struct { + const char* _Nonnull name; + int64_t value; +} AHardwareBufferLongOptions; + +enum AHardwareBufferStatus : int32_t { + /* Success, no error */ + AHARDWAREBUFFER_STATUS_OK = 0, + /* There's insufficient memory to satisfy the request */ + AHARDWAREBUFFER_STATUS_NO_MEMORY = -ENOMEM, + /* The given argument is invalid */ + AHARDWAREBUFFER_STATUS_BAD_VALUE = -EINVAL, + /* The requested operation is not supported by the device */ + AHARDWAREBUFFER_STATUS_UNSUPPORTED = -ENOSYS, + /* An unknown error occurred */ + AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR = (-2147483647 - 1), +}; + +/** + * Allocates a buffer that matches the passed AHardwareBuffer_Desc with additional options + * + * If allocation succeeds, the buffer can be used according to the + * usage flags specified in its description. If a buffer is used in ways + * not compatible with its usage flags, the results are undefined and + * may include program termination. + * + * @param desc The AHardwareBuffer_Desc that describes the allocation to request. Note that `stride` + * is ignored. + * @param additionalOptions A pointer to an array of AHardwareBufferLongOptions with additional + * string key + long value options that may be specified. May be null if + * `additionalOptionsSize` is 0 + * @param additionalOptionsSize The number of additional options to pass + * @param outBuffer The resulting buffer allocation + * @return AHARDWAREBUFFER_STATUS_OK on success + * AHARDWAREBUFFER_STATUS_NO_MEMORY if there's insufficient resources for the allocation + * AHARDWAREBUFFER_STATUS_BAD_VALUE if the provided description & options are not supported + * by the device + * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other error + * any reason. The returned buffer has a reference count of 1. + */ +enum AHardwareBufferStatus AHardwareBuffer_allocateWithOptions( + const AHardwareBuffer_Desc* _Nonnull desc, + const AHardwareBufferLongOptions* _Nullable additionalOptions, size_t additionalOptionsSize, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Queries the dataspace of the given AHardwareBuffer. + * + * @param buffer The non-null buffer for which to query the Dataspace + * @return The dataspace of the buffer, or ADATASPACE_UNKNOWN if one hasn't been set + */ +enum ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the dataspace of the given AHardwareBuffer + * @param buffer The non-null buffer for which to set the dataspace + * @param dataSpace The dataspace to set + * @return AHARDWAREBUFFER_STATUS_OK on success, + * AHARDWAREBUFFER_STATUS_UNSUPPORTED if the device doesn't support setting the dataspace, + * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other failure. + */ +enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* _Nonnull buffer, + enum ADataSpace dataSpace) + __INTRODUCED_IN(__ANDROID_API_V__); + __END_DECLS #endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 95fc920da1..e29d5a6bb4 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,6 +2,7 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; + AHardwareBuffer_allocateWithOptions; # llndk systemapi AHardwareBuffer_createFromHandle; # llndk systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 @@ -16,6 +17,8 @@ LIBNATIVEWINDOW { AHardwareBuffer_unlock; AHardwareBuffer_readFromParcel; # introduced=34 AHardwareBuffer_writeToParcel; # introduced=34 + AHardwareBuffer_getDataSpace; # llndk systemapi + AHardwareBuffer_setDataSpace; # llndk systemapi ANativeWindowBuffer_getHardwareBuffer; # llndk ANativeWindow_OemStorageGet; # llndk ANativeWindow_OemStorageSet; # llndk diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp index 798d804275..97740dbcd8 100644 --- a/libs/nativewindow/rust/Android.bp +++ b/libs/nativewindow/rust/Android.bp @@ -16,6 +16,7 @@ package { default_applicable_licenses: [ "frameworks_native_libs_nativewindow_license", ], + default_team: "trendy_team_android_core_graphics_stack", } rust_bindgen { diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp index ef863b6d67..136395aac0 100644 --- a/libs/nativewindow/tests/AHardwareBufferTest.cpp +++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "AHardwareBuffer_test" //#define LOG_NDEBUG 0 +#include <android-base/properties.h> +#include <android/data_space.h> #include <android/hardware/graphics/common/1.0/types.h> #include <gtest/gtest.h> #include <private/android/AHardwareBufferHelpers.h> @@ -26,6 +28,10 @@ using namespace android; using android::hardware::graphics::common::V1_0::BufferUsage; +static bool IsCuttlefish() { + return ::android::base::GetProperty("ro.product.board", "") == "cutf"; +} + static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected, uint64_t actual, const char* type) { std::ostringstream ss; @@ -170,3 +176,83 @@ TEST(AHardwareBufferTest, GetIdTest) { EXPECT_NE(id1, id2); } + +TEST(AHardwareBufferTest, Allocate2NoExtras) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 1, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_BLOB, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + ASSERT_EQ(0, AHardwareBuffer_allocateWithOptions(&desc, nullptr, 0, &buffer)); + uint64_t id = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); + EXPECT_NE(0, id); + AHardwareBuffer_Desc desc2{}; + AHardwareBuffer_describe(buffer, &desc2); + EXPECT_EQ(desc.width, desc2.width); + EXPECT_EQ(desc.height, desc2.height); + EXPECT_GE(desc2.stride, desc2.width); + + AHardwareBuffer_release(buffer); +} + +TEST(AHardwareBufferTest, Allocate2WithExtras) { + if (!IsCuttlefish()) { + GTEST_SKIP() << "Unknown gralloc HAL, cannot test extras"; + } + + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 48, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + std::array<AHardwareBufferLongOptions, 1> extras = {{ + {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3}, + }}; + ASSERT_EQ(0, AHardwareBuffer_allocateWithOptions(&desc, extras.data(), extras.size(), &buffer)); + uint64_t id = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); + EXPECT_NE(0, id); + AHardwareBuffer_Desc desc2{}; + AHardwareBuffer_describe(buffer, &desc2); + EXPECT_EQ(desc.width, desc2.width); + EXPECT_EQ(desc.height, desc2.height); + EXPECT_GE(desc2.stride, desc2.width); + + EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer)); + + AHardwareBuffer_release(buffer); +} + +TEST(AHardwareBufferTest, GetSetDataspace) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 48, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + ASSERT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer)); + + EXPECT_EQ(ADATASPACE_UNKNOWN, AHardwareBuffer_getDataSpace(buffer)); + AHardwareBufferStatus status = AHardwareBuffer_setDataSpace(buffer, ADATASPACE_DISPLAY_P3); + if (status != AHARDWAREBUFFER_STATUS_UNSUPPORTED) { + EXPECT_EQ(0, status); + EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer)); + } + + AHardwareBuffer_release(buffer); +}
\ No newline at end of file diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp index 30737c1bf6..a4ebcaca0e 100644 --- a/libs/nativewindow/tests/Android.bp +++ b/libs/nativewindow/tests/Android.bp @@ -23,6 +23,7 @@ package { default_applicable_licenses: [ "frameworks_native_libs_nativewindow_license", ], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { @@ -31,6 +32,7 @@ cc_test { "device-tests", ], shared_libs: [ + "libbase", "libgui", "liblog", "libnativewindow", @@ -44,5 +46,8 @@ cc_test { "ANativeWindowTest.cpp", "c_compatibility.c", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } diff --git a/libs/nativewindow/tests/benchmark/Android.bp b/libs/nativewindow/tests/benchmark/Android.bp index 6f844cf864..b815d80142 100644 --- a/libs/nativewindow/tests/benchmark/Android.bp +++ b/libs/nativewindow/tests/benchmark/Android.bp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", +} + cc_defaults { name: "nativewindow_benchmark_defaults_cc", shared_libs: ["libnativewindow"], diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index ba2eb7d224..b501d40f26 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { @@ -48,11 +49,19 @@ cc_defaults { static_libs: [ "libshaders", "libtonemap", + "libsurfaceflinger_common", ], local_include_dirs: ["include"], export_include_dirs: ["include"], } +// Needed by FlagManager to access a #define. +cc_library_static { + name: "librenderengine_includes", + local_include_dirs: ["include"], + export_include_dirs: ["include"], +} + filegroup { name: "librenderengine_sources", srcs: [ diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 3e1ac33d57..233134d2db 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -28,28 +28,28 @@ namespace android { namespace renderengine { std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { - switch (args.renderEngineType) { - case RenderEngineType::SKIA_GL: + if (args.threaded == Threaded::YES) { + switch (args.graphicsApi) { + case GraphicsApi::GL: + ALOGD("Threaded RenderEngine with SkiaGL Backend"); + return renderengine::threaded::RenderEngineThreaded::create([args]() { + return android::renderengine::skia::SkiaGLRenderEngine::create(args); + }); + case GraphicsApi::VK: + ALOGD("Threaded RenderEngine with SkiaVK Backend"); + return renderengine::threaded::RenderEngineThreaded::create([args]() { + return android::renderengine::skia::SkiaVkRenderEngine::create(args); + }); + } + } + + switch (args.graphicsApi) { + case GraphicsApi::GL: ALOGD("RenderEngine with SkiaGL Backend"); return renderengine::skia::SkiaGLRenderEngine::create(args); - case RenderEngineType::SKIA_VK: + case GraphicsApi::VK: ALOGD("RenderEngine with SkiaVK Backend"); return renderengine::skia::SkiaVkRenderEngine::create(args); - case RenderEngineType::SKIA_GL_THREADED: { - ALOGD("Threaded RenderEngine with SkiaGL Backend"); - return renderengine::threaded::RenderEngineThreaded::create( - [args]() { - return android::renderengine::skia::SkiaGLRenderEngine::create(args); - }, - args.renderEngineType); - } - case RenderEngineType::SKIA_VK_THREADED: - ALOGD("Threaded RenderEngine with SkiaVK Backend"); - return renderengine::threaded::RenderEngineThreaded::create( - [args]() { - return android::renderengine::skia::SkiaVkRenderEngine::create(args); - }, - args.renderEngineType); } } diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp index 55c34cd059..e1a6f6af0d 100644 --- a/libs/renderengine/benchmark/Android.bp +++ b/libs/renderengine/benchmark/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_benchmark { @@ -37,6 +38,7 @@ cc_benchmark { static_libs: [ "librenderengine", "libshaders", + "libsurfaceflinger_common", "libtonemap", ], cflags: [ @@ -54,6 +56,7 @@ cc_benchmark { "libsync", "libui", "libutils", + "server_configurable_flags", ], data: ["resources/*"], diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index a7f1df9a9b..101f519e55 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -30,46 +30,6 @@ using namespace android; using namespace android::renderengine; /////////////////////////////////////////////////////////////////////////////// -// Helpers for Benchmark::Apply -/////////////////////////////////////////////////////////////////////////////// - -std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) { - switch (type) { - case RenderEngine::RenderEngineType::SKIA_GL_THREADED: - return "skiaglthreaded"; - case RenderEngine::RenderEngineType::SKIA_GL: - return "skiagl"; - case RenderEngine::RenderEngineType::SKIA_VK: - return "skiavk"; - case RenderEngine::RenderEngineType::SKIA_VK_THREADED: - return "skiavkthreaded"; - } -} - -/** - * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a - * Benchmark which specifies which RenderEngineType it uses. - * - * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make - * it obvious which version is being run. - * - * @param b The benchmark family - * @param type The type of RenderEngine to use. - */ -static void AddRenderEngineType(benchmark::internal::Benchmark* b, - RenderEngine::RenderEngineType type) { - b->Arg(static_cast<int64_t>(type)); - b->ArgName(RenderEngineTypeName(type)); -} - -/** - * Run a benchmark once using SKIA_GL_THREADED. - */ -static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) { - AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED); -} - -/////////////////////////////////////////////////////////////////////////////// // Helpers for calling drawLayers /////////////////////////////////////////////////////////////////////////////// @@ -104,7 +64,8 @@ std::pair<uint32_t, uint32_t> getDisplaySize() { return std::pair<uint32_t, uint32_t>(width, height); } -static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) { +static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded, + RenderEngine::GraphicsApi graphicsApi) { auto args = RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) .setImageCacheSize(1) @@ -112,7 +73,8 @@ static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngi .setPrecacheToneMapperShaderOnly(false) .setSupportsBackgroundBlur(true) .setContextPriority(RenderEngine::ContextPriority::REALTIME) - .setRenderEngineType(type) + .setThreaded(threaded) + .setGraphicsApi(graphicsApi) .build(); return RenderEngine::create(args); } @@ -214,8 +176,11 @@ static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& // Benchmarks /////////////////////////////////////////////////////////////////////////////// -void BM_blur(benchmark::State& benchState) { - auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range())); +template <class... Args> +void BM_blur(benchmark::State& benchState, Args&&... args) { + auto args_tuple = std::make_tuple(std::move(args)...); + auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); // Initially use cpu access so we can decode into it with AImageDecoder. auto [width, height] = getDisplaySize(); @@ -259,4 +224,5 @@ void BM_blur(benchmark::State& benchState) { benchDrawLayers(*re, layers, benchState, "blurred"); } -BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded); +BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL); diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index b41fa1dd69..de05268a67 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -92,15 +92,20 @@ public: REALTIME = 4, }; - enum class RenderEngineType { - SKIA_GL = 3, - SKIA_GL_THREADED = 4, - SKIA_VK = 5, - SKIA_VK_THREADED = 6, + enum class Threaded { + NO, + YES, + }; + + enum class GraphicsApi { + GL, + VK, }; static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); + static bool canSupport(GraphicsApi); + virtual ~RenderEngine() = 0; // ----- BEGIN DEPRECATED INTERFACE ----- @@ -176,10 +181,9 @@ public: // query is required to be thread safe. virtual bool supportsBackgroundBlur() = 0; - // Returns the current type of RenderEngine instance that was created. // TODO(b/180767535): This is only implemented to allow for backend-specific behavior, which // we should not allow in general, so remove this. - RenderEngineType getRenderEngineType() const { return mRenderEngineType; } + bool isThreaded() const { return mThreaded == Threaded::YES; } static void validateInputBufferUsage(const sp<GraphicBuffer>&); static void validateOutputBufferUsage(const sp<GraphicBuffer>&); @@ -191,9 +195,9 @@ public: virtual void setEnableTracing(bool /*tracingEnabled*/) {} protected: - RenderEngine() : RenderEngine(RenderEngineType::SKIA_GL) {} + RenderEngine() : RenderEngine(Threaded::NO) {} - RenderEngine(RenderEngineType type) : mRenderEngineType(type) {} + RenderEngine(Threaded threaded) : mThreaded(threaded) {} // Maps GPU resources for this buffer. // Note that work may be deferred to an additional thread, i.e. this call @@ -228,7 +232,7 @@ protected: friend class impl::ExternalTexture; friend class threaded::RenderEngineThreaded; friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test; - const RenderEngineType mRenderEngineType; + const Threaded mThreaded; // Update protectedContext mode depending on whether or not any layer has a protected buffer. void updateProtectedContext(const std::vector<LayerSettings>&, @@ -251,7 +255,8 @@ struct RenderEngineCreationArgs { bool precacheToneMapperShaderOnly; bool supportsBackgroundBlur; RenderEngine::ContextPriority contextPriority; - RenderEngine::RenderEngineType renderEngineType; + RenderEngine::Threaded threaded; + RenderEngine::GraphicsApi graphicsApi; struct Builder; @@ -261,14 +266,16 @@ private: bool _enableProtectedContext, bool _precacheToneMapperShaderOnly, bool _supportsBackgroundBlur, RenderEngine::ContextPriority _contextPriority, - RenderEngine::RenderEngineType _renderEngineType) + RenderEngine::Threaded _threaded, + RenderEngine::GraphicsApi _graphicsApi) : pixelFormat(_pixelFormat), imageCacheSize(_imageCacheSize), enableProtectedContext(_enableProtectedContext), precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly), supportsBackgroundBlur(_supportsBackgroundBlur), contextPriority(_contextPriority), - renderEngineType(_renderEngineType) {} + threaded(_threaded), + graphicsApi(_graphicsApi) {} RenderEngineCreationArgs() = delete; }; @@ -299,14 +306,18 @@ struct RenderEngineCreationArgs::Builder { this->contextPriority = contextPriority; return *this; } - Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) { - this->renderEngineType = renderEngineType; + Builder& setThreaded(RenderEngine::Threaded threaded) { + this->threaded = threaded; + return *this; + } + Builder& setGraphicsApi(RenderEngine::GraphicsApi graphicsApi) { + this->graphicsApi = graphicsApi; return *this; } RenderEngineCreationArgs build() const { return RenderEngineCreationArgs(pixelFormat, imageCacheSize, enableProtectedContext, precacheToneMapperShaderOnly, supportsBackgroundBlur, - contextPriority, renderEngineType); + contextPriority, threaded, graphicsApi); } private: @@ -317,8 +328,8 @@ private: bool precacheToneMapperShaderOnly = false; bool supportsBackgroundBlur = false; RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM; - RenderEngine::RenderEngineType renderEngineType = - RenderEngine::RenderEngineType::SKIA_GL_THREADED; + RenderEngine::Threaded threaded = RenderEngine::Threaded::YES; + RenderEngine::GraphicsApi graphicsApi = RenderEngine::GraphicsApi::GL; }; } // namespace renderengine diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 2053c6a34f..fea4129ec0 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -236,7 +236,13 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo err = selectEGLConfig(display, format, 0, &config); if (err != NO_ERROR) { // this EGL is too lame for android - LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up" + " (format: %d, vendor: %s, version: %s, extensions: %s, Client" + " API: %s)", + format, eglQueryString(display, EGL_VENDOR), + eglQueryString(display, EGL_VERSION), + eglQueryString(display, EGL_EXTENSIONS), + eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); } } } @@ -262,7 +268,7 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext, EGLSurface protectedPlaceholder) - : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat), args.supportsBackgroundBlur), mEGLDisplay(display), mEGLContext(ctxt), diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 88326e77d7..6e393f03fa 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -53,6 +53,7 @@ #include <SkSurface.h> #include <SkTileMode.h> #include <android-base/stringprintf.h> +#include <common/FlagManager.h> #include <gui/FenceMonitor.h> #include <gui/TraceUtils.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> @@ -269,9 +270,9 @@ void SkiaRenderEngine::setEnableTracing(bool tracingEnabled) { SkAndroidFrameworkTraceUtil::setEnableTracing(tracingEnabled); } -SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, +SkiaRenderEngine::SkiaRenderEngine(Threaded threaded, PixelFormat pixelFormat, bool supportsBackgroundBlur) - : RenderEngine(type), mDefaultPixelFormat(pixelFormat) { + : RenderEngine(threaded), mDefaultPixelFormat(pixelFormat) { if (supportsBackgroundBlur) { ALOGD("Background Blurs Enabled"); mBlurFilter = new KawaseBlurFilter(); @@ -389,10 +390,9 @@ void SkiaRenderEngine::ensureGrContextsCreated() { void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) { // Only run this if RE is running on its own thread. This - // way the access to GL operations is guaranteed to be happening on the + // way the access to GL/VK operations is guaranteed to be happening on the // same thread. - if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED && - mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) { + if (!isThreaded()) { return; } // We don't attempt to map a buffer if the buffer contains protected content. In GL this is @@ -420,6 +420,9 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, mGraphicBufferExternalRefs[buffer->getId()]++; if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) { + if (FlagManager::getInstance().renderable_buffer_usage()) { + isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER; + } std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(grContext, buffer->toAHardwareBuffer(), @@ -761,10 +764,11 @@ void SkiaRenderEngine::drawLayersInternal( // save a snapshot of the activeSurface to use as input to the blur shaders blurInput = activeSurface->makeImageSnapshot(); - // blit the offscreen framebuffer into the destination AHB, but only - // if there are blur regions. backgroundBlurRadius blurs the entire - // image below, so it can skip this step. - if (layer.blurRegions.size()) { + // blit the offscreen framebuffer into the destination AHB. This ensures that + // even if the blurred image does not cover the screen (for example, during + // a rotation animation, or if blur regions are used), the entire screen is + // initialized. + if (layer.blurRegions.size() || FlagManager::getInstance().restore_blur_step()) { SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); if (CC_UNLIKELY(mCapture->isCaptureRunning())) { diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index ac134afa64..e88d44cca6 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -59,7 +59,7 @@ class BlurFilter; class SkiaRenderEngine : public RenderEngine { public: static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args); - SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, bool supportsBackgroundBlur); + SkiaRenderEngine(Threaded, PixelFormat pixelFormat, bool supportsBackgroundBlur); ~SkiaRenderEngine() override; std::future<void> primeCache(bool shouldPrimeUltraHDR) override final; diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index ba20d1f223..eb7a9d5bfa 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -34,6 +34,8 @@ #include <cstdint> #include <memory> +#include <sstream> +#include <string> #include <vector> #include <vulkan/vulkan.h> @@ -67,6 +69,13 @@ struct DestroySemaphoreInfo { 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; @@ -79,6 +88,7 @@ struct VulkanInterface { VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr; VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr; VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr; + VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures = nullptr; GrVkGetProc grGetProc; bool isProtected; bool isRealtimePriority; @@ -100,6 +110,8 @@ struct VulkanInterface { 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; }; @@ -178,6 +190,68 @@ struct VulkanInterface { } }; +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); @@ -213,6 +287,7 @@ static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkD CHECK_NONNULL(vk##F) VulkanInterface initVulkanInterface(bool protectedContent = false) { + const nsecs_t timeBefore = systemTime(); VulkanInterface interface; VK_GET_PROC(EnumerateInstanceVersion); @@ -303,6 +378,11 @@ VulkanInterface initVulkanInterface(bool protectedContent = false) { 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, @@ -429,6 +509,14 @@ VulkanInterface initVulkanInterface(bool protectedContent = false) { 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; @@ -516,7 +604,9 @@ VulkanInterface initVulkanInterface(bool protectedContent = false) { interface.isProtected = protectedContent; // funcs already initialized - ALOGD("%s: Success init Vulkan interface", __func__); + 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; } @@ -545,6 +635,10 @@ void teardownVulkanInterface(VulkanInterface* interface) { delete interface->physicalDeviceFeatures2; } + if (interface->deviceFaultFeatures) { + delete interface->deviceFaultFeatures; + } + interface->samplerYcbcrConversionFeatures = nullptr; interface->physicalDeviceFeatures2 = nullptr; interface->protectedMemoryFeatures = nullptr; @@ -568,17 +662,25 @@ static void sSetupVulkanInterface() { } } +bool RenderEngine::canSupport(GraphicsApi graphicsApi) { + switch (graphicsApi) { + case GraphicsApi::GL: + return true; + case GraphicsApi::VK: { + if (!sVulkanInterface.initialized) { + sVulkanInterface = initVulkanInterface(false /* no protected content */); + ALOGD("%s: initialized == %s.", __func__, + sVulkanInterface.initialized ? "true" : "false"); + } + return sVulkanInterface.initialized; + } + } +} + namespace skia { using base::StringAppendF; -bool SkiaVkRenderEngine::canSupportSkiaVkRenderEngine() { - VulkanInterface temp = initVulkanInterface(false /* no protected content */); - ALOGD("SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(): initialized == %s.", - temp.initialized ? "true" : "false"); - return temp.initialized; -} - std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args)); @@ -596,7 +698,7 @@ std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( } SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args) - : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat), args.supportsBackgroundBlur) {} SkiaVkRenderEngine::~SkiaVkRenderEngine() { diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index 2e0cf45220..52bc500a5a 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -27,8 +27,6 @@ namespace skia { class SkiaVkRenderEngine : public SkiaRenderEngine { public: - // Returns false if Vulkan implementation can't support SkiaVkRenderEngine. - static bool canSupportSkiaVkRenderEngine(); static std::unique_ptr<SkiaVkRenderEngine> create(const RenderEngineCreationArgs& args); ~SkiaVkRenderEngine() override; diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index 5c9820cdc5..09f09a697a 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -111,7 +111,7 @@ sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, constexpr int kSampleCount = 1; constexpr bool kMipmapped = false; constexpr SkSurfaceProps* kProps = nullptr; - sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, scaledInfo, + sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kNo, scaledInfo, kSampleCount, kTopLeft_GrSurfaceOrigin, kProps, kMipmapped, input->isProtected()); LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__); diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 50e166d2a7..0eea187407 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { @@ -44,6 +45,7 @@ cc_test { "librenderengine_mocks", "libshaders", "libtonemap", + "libsurfaceflinger_common", ], header_libs: [ "libtonemap_headers", @@ -61,5 +63,6 @@ cc_test { "libsync", "libui", "libutils", + "server_configurable_flags", ], } diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 11d4fdebdc..7b8eb8470f 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -106,25 +106,9 @@ public: virtual ~RenderEngineFactory() = default; virtual std::string name() = 0; - virtual renderengine::RenderEngine::RenderEngineType type() = 0; - virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0; - virtual bool typeSupported() = 0; -}; - -class SkiaVkRenderEngineFactory : public RenderEngineFactory { -public: - std::string name() override { return "SkiaVkRenderEngineFactory"; } - - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_VK; - } - - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - std::unique_ptr<renderengine::RenderEngine> re = createSkiaVkRenderEngine(); - return re; - } - - std::unique_ptr<renderengine::skia::SkiaVkRenderEngine> createSkiaVkRenderEngine() { + virtual renderengine::RenderEngine::GraphicsApi graphicsApi() = 0; + bool apiSupported() { return renderengine::RenderEngine::canSupport(graphicsApi()); } + std::unique_ptr<renderengine::RenderEngine> createRenderEngine() { renderengine::RenderEngineCreationArgs reCreationArgs = renderengine::RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) @@ -133,65 +117,29 @@ public: .setPrecacheToneMapperShaderOnly(false) .setSupportsBackgroundBlur(true) .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) + .setThreaded(renderengine::RenderEngine::Threaded::NO) + .setGraphicsApi(graphicsApi()) .build(); - return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs); + return renderengine::RenderEngine::create(reCreationArgs); } - - bool typeSupported() override { - return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(); - } - void skip() { GTEST_SKIP(); } }; -class SkiaGLESRenderEngineFactory : public RenderEngineFactory { +class SkiaVkRenderEngineFactory : public RenderEngineFactory { public: - std::string name() override { return "SkiaGLRenderEngineFactory"; } - - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL; - } + std::string name() override { return "SkiaVkRenderEngineFactory"; } - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - renderengine::RenderEngineCreationArgs reCreationArgs = - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) - .build(); - return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); + renderengine::RenderEngine::GraphicsApi graphicsApi() override { + return renderengine::RenderEngine::GraphicsApi::VK; } - - bool typeSupported() override { return true; } }; -class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory { +class SkiaGLESRenderEngineFactory : public RenderEngineFactory { public: - std::string name() override { return "SkiaGLCMRenderEngineFactory"; } + std::string name() override { return "SkiaGLRenderEngineFactory"; } - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL; + renderengine::RenderEngine::GraphicsApi graphicsApi() { + return renderengine::RenderEngine::GraphicsApi::GL; } - - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - renderengine::RenderEngineCreationArgs reCreationArgs = - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) - .build(); - return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); - } - - bool typeSupported() override { return true; } }; class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> { @@ -1526,7 +1474,7 @@ INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, std::make_shared<SkiaVkRenderEngineFactory>())); TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1534,7 +1482,7 @@ TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { } TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1561,7 +1509,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { } TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1595,7 +1543,7 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { } TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1616,7 +1564,7 @@ TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1624,7 +1572,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1632,7 +1580,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1640,7 +1588,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1648,7 +1596,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1656,7 +1604,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1664,7 +1612,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1672,7 +1620,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1680,7 +1628,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1688,7 +1636,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1696,7 +1644,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1706,7 +1654,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1717,7 +1665,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1726,7 +1674,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1734,7 +1682,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1742,7 +1690,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_color } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1750,7 +1698,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1758,7 +1706,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) } TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1766,7 +1714,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1774,7 +1722,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1782,7 +1730,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1790,7 +1738,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1798,7 +1746,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1806,7 +1754,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1814,7 +1762,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1822,7 +1770,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSourc } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1830,7 +1778,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1838,7 +1786,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1846,7 +1794,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1856,7 +1804,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1867,7 +1815,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_o TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1876,7 +1824,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_o } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1884,7 +1832,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1892,7 +1840,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqu } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1900,7 +1848,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1908,7 +1856,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBuffer } TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1916,7 +1864,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1924,7 +1872,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1932,7 +1880,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1940,7 +1888,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1948,7 +1896,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1956,7 +1904,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1964,7 +1912,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1972,7 +1920,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1980,7 +1928,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1988,7 +1936,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1996,7 +1944,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2006,7 +1954,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -2017,7 +1965,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_b TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -2026,7 +1974,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_b } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2034,7 +1982,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2042,7 +1990,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_buffe } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2050,7 +1998,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2058,7 +2006,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource } TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2066,7 +2014,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2074,7 +2022,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { } TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2082,7 +2030,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { } TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2090,7 +2038,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2108,7 +2056,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2131,7 +2079,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2155,7 +2103,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2180,7 +2128,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2206,7 +2154,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2235,7 +2183,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { } TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2271,7 +2219,7 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { if (mRE->canSkipPostRenderCleanup()) { // Skia's Vk backend may keep the texture alive beyond drawLayersInternal, so // it never gets added to the cleanup list. In those cases, we can skip. - EXPECT_TRUE(GetParam()->type() == renderengine::RenderEngine::RenderEngineType::SKIA_VK); + EXPECT_TRUE(GetParam()->graphicsApi() == renderengine::RenderEngine::GraphicsApi::VK); } else { mRE->cleanupPostRender(); EXPECT_TRUE(mRE->canSkipPostRenderCleanup()); @@ -2279,7 +2227,7 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { } TEST_P(RenderEngineTest, testRoundedCornersCrop) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2332,7 +2280,7 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2380,7 +2328,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2417,7 +2365,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { } TEST_P(RenderEngineTest, testRoundedCornersXY) { - if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2460,7 +2408,7 @@ TEST_P(RenderEngineTest, testRoundedCornersXY) { } TEST_P(RenderEngineTest, testClear) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2492,7 +2440,7 @@ TEST_P(RenderEngineTest, testClear) { } TEST_P(RenderEngineTest, testDisableBlendingBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2543,7 +2491,7 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) { } TEST_P(RenderEngineTest, testBorder) { - if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2588,7 +2536,7 @@ TEST_P(RenderEngineTest, testBorder) { } TEST_P(RenderEngineTest, testDimming) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2663,7 +2611,7 @@ TEST_P(RenderEngineTest, testDimming) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2741,7 +2689,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2804,7 +2752,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_deviceHandles) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2868,7 +2816,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_devi } TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2922,7 +2870,7 @@ TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { } TEST_P(RenderEngineTest, test_isOpaque) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2972,7 +2920,7 @@ TEST_P(RenderEngineTest, test_isOpaque) { } TEST_P(RenderEngineTest, test_tonemapPQMatches) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2989,7 +2937,7 @@ TEST_P(RenderEngineTest, test_tonemapPQMatches) { } TEST_P(RenderEngineTest, test_tonemapHLGMatches) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -3006,7 +2954,7 @@ TEST_P(RenderEngineTest, test_tonemapHLGMatches) { } TEST_P(RenderEngineTest, r8_behaves_as_mask) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3066,7 +3014,7 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { } TEST_P(RenderEngineTest, r8_respects_color_transform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3131,7 +3079,7 @@ TEST_P(RenderEngineTest, r8_respects_color_transform) { } TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3199,7 +3147,7 @@ TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { } TEST_P(RenderEngineTest, primeShaderCache) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp index 1b9adba063..d56dbb2a7b 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -35,8 +35,7 @@ struct RenderEngineThreadedTest : public ::testing::Test { void SetUp() override { mThreadedRE = renderengine::threaded::RenderEngineThreaded::create( - [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }, - renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED); + [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }); } std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE; diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index 367bee89f9..f4cebc05ec 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -33,13 +33,12 @@ namespace android { namespace renderengine { namespace threaded { -std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory, - RenderEngineType type) { - return std::make_unique<RenderEngineThreaded>(std::move(factory), type); +std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) { + return std::make_unique<RenderEngineThreaded>(std::move(factory)); } -RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type) - : RenderEngine(type) { +RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) + : RenderEngine(Threaded::YES) { ATRACE_CALL(); std::lock_guard lockThread(mThreadMutex); @@ -125,8 +124,10 @@ void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_S } void RenderEngineThreaded::waitUntilInitialized() const { - std::unique_lock<std::mutex> lock(mInitializedMutex); - mInitializedCondition.wait(lock, [=] { return mIsInitialized; }); + if (!mIsInitialized) { + std::unique_lock<std::mutex> lock(mInitializedMutex); + mInitializedCondition.wait(lock, [this] { return mIsInitialized.load(); }); + } } std::future<void> RenderEngineThreaded::primeCache(bool shouldPrimeUltraHDR) { diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index 74af2bd776..d440c961e7 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -37,10 +37,9 @@ using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::Render */ class RenderEngineThreaded : public RenderEngine { public: - static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory, - RenderEngineType type); + static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory); - RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type); + RenderEngineThreaded(CreateInstanceFactory factory); ~RenderEngineThreaded() override; std::future<void> primeCache(bool shouldPrimeUltraHDR) override; @@ -97,7 +96,7 @@ private: // Used to allow select thread safe methods to be accessed without requiring the // method to be invoked on the RenderEngine thread - bool mIsInitialized = false; + std::atomic_bool mIsInitialized = false; mutable std::mutex mInitializedMutex; mutable std::condition_variable mInitializedCondition; diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index d992aa5105..7fa47b45f0 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -21,6 +21,19 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "libsensor_flags", + package: "com.android.hardware.libsensor.flags", + container: "system", + srcs: ["libsensor_flags.aconfig"], +} + +cc_aconfig_library { + name: "libsensor_flags_c_lib", + host_supported: true, + aconfig_declarations: "libsensor_flags", +} + cc_library { name: "libsensor", @@ -52,6 +65,10 @@ cc_library { "android.companion.virtual.virtualdevice_aidl-cpp", ], + static_libs: [ + "libsensor_flags_c_lib", + ], + export_include_dirs: ["include"], export_shared_lib_headers: [ diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index d112a1265c..9411e204e9 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -37,6 +37,8 @@ #include <sensor/Sensor.h> #include <sensor/SensorEventQueue.h> +#include <com_android_hardware_libsensor_flags.h> + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -88,49 +90,51 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) SensorManager* sensorManager; auto iterator = sPackageInstances.find(packageName); + const uid_t uid = IPCThreadState::self()->getCallingUid(); + const int deviceId = getDeviceIdForUid(uid); + + // Return the cached instance if the device association of the package has not changed. if (iterator != sPackageInstances.end()) { sensorManager = iterator->second; - } else { - String16 opPackageName = packageName; - const uid_t uid = IPCThreadState::self()->getCallingUid(); - - // It is possible that the calling code has no access to the package name. - // In this case we will get the packages for the calling UID and pick the - // first one for attributing the app op. This will work correctly for - // runtime permissions as for legacy apps we will toggle the app op for - // all packages in the UID. The caveat is that the operation may be attributed - // to the wrong package and stats based on app ops may be slightly off. - if (opPackageName.size() <= 0) { - sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != nullptr) { - Vector<String16> packages; - interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); - if (!packages.isEmpty()) { - opPackageName = packages[0]; - } else { - ALOGE("No packages for calling UID"); - } + if (sensorManager->mDeviceId == deviceId) { + return *sensorManager; + } + } + + // It is possible that the calling code has no access to the package name. + // In this case we will get the packages for the calling UID and pick the + // first one for attributing the app op. This will work correctly for + // runtime permissions as for legacy apps we will toggle the app op for + // all packages in the UID. The caveat is that the operation may be attributed + // to the wrong package and stats based on app ops may be slightly off. + String16 opPackageName = packageName; + if (opPackageName.size() <= 0) { + sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); + if (binder != nullptr) { + Vector<String16> packages; + interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); + if (!packages.isEmpty()) { + opPackageName = packages[0]; } else { - ALOGE("Cannot get permission service"); + ALOGE("No packages for calling UID"); } + } else { + ALOGE("Cannot get permission service"); } + } - // Check if the calling UID is observed on a virtual device. If so, provide that device's - // sensors by default instead of the default device's sensors. - const int deviceId = getDeviceIdForUid(uid); - sensorManager = new SensorManager(opPackageName, deviceId); - - // If we had no package name, we looked it up from the UID and the sensor - // manager instance we created should also be mapped to the empty package - // name, to avoid looking up the packages for a UID and get the same result. - if (packageName.size() <= 0) { - sPackageInstances.insert(std::make_pair(String16(), sensorManager)); - } + sensorManager = new SensorManager(opPackageName, deviceId); - // Stash the per package sensor manager. - sPackageInstances.insert(std::make_pair(opPackageName, sensorManager)); + // If we had no package name, we looked it up from the UID and the sensor + // manager instance we created should also be mapped to the empty package + // name, to avoid looking up the packages for a UID and get the same result. + if (packageName.size() <= 0) { + sPackageInstances.insert(std::make_pair(String16(), sensorManager)); } + // Stash the per package sensor manager. + sPackageInstances.insert(std::make_pair(opPackageName, sensorManager)); + return *sensorManager; } @@ -190,6 +194,9 @@ void SensorManager::sensorManagerDied() { } status_t SensorManager::assertStateLocked() { +#if COM_ANDROID_HARDWARE_LIBSENSOR_FLAGS(SENSORMANAGER_PING_BINDER) + if (mSensorServer == nullptr) { +#else bool initSensorManager = false; if (mSensorServer == nullptr) { initSensorManager = true; @@ -201,6 +208,7 @@ status_t SensorManager::assertStateLocked() { } } if (initSensorManager) { +#endif waitForSensorService(&mSensorServer); LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL"); @@ -248,6 +256,22 @@ ssize_t SensorManager::getSensorList(Sensor const* const** list) { return static_cast<ssize_t>(mSensors.size()); } +ssize_t SensorManager::getDefaultDeviceSensorList(Vector<Sensor> & list) { + Mutex::Autolock _l(mLock); + status_t err = assertStateLocked(); + if (err < 0) { + return static_cast<ssize_t>(err); + } + + if (mDeviceId == DEVICE_ID_DEFAULT) { + list = mSensors; + } else { + list = mSensorServer->getSensorList(mOpPackageName); + } + + return static_cast<ssize_t>(list.size()); +} + ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) { Mutex::Autolock _l(mLock); status_t err = assertStateLocked(); diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index e67fac7617..49f050a0ac 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h @@ -58,6 +58,7 @@ public: ~SensorManager(); ssize_t getSensorList(Sensor const* const** list); + ssize_t getDefaultDeviceSensorList(Vector<Sensor> & list); ssize_t getDynamicSensorList(Vector<Sensor>& list); ssize_t getDynamicSensorList(Sensor const* const** list); ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list); diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig new file mode 100644 index 0000000000..c511f4a72f --- /dev/null +++ b/libs/sensor/libsensor_flags.aconfig @@ -0,0 +1,10 @@ +package: "com.android.hardware.libsensor.flags" +container: "system" + +flag { + name: "sensormanager_ping_binder" + namespace: "sensors" + description: "Whether to pingBinder on SensorManager init" + bug: "322228259" + is_fixed_read_only: true +} diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp index 00514c4417..1e7e70775a 100644 --- a/libs/sensorprivacy/Android.bp +++ b/libs/sensorprivacy/Android.bp @@ -57,6 +57,7 @@ cc_library_shared { filegroup { name: "libsensorprivacy_aidl", srcs: [ + "aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl", "aidl/android/hardware/ISensorPrivacyListener.aidl", "aidl/android/hardware/ISensorPrivacyManager.aidl", ], diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp index 57c74ee565..fe9378616d 100644 --- a/libs/sensorprivacy/SensorPrivacyManager.cpp +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -108,7 +108,7 @@ bool SensorPrivacyManager::isSensorPrivacyEnabled() bool SensorPrivacyManager::isToggleSensorPrivacyEnabled(int sensor) { - sp<hardware::ISensorPrivacyManager> service = getService(); + sp<hardware::ISensorPrivacyManager> service = getService(); if (service != nullptr) { bool result; service->isCombinedToggleSensorPrivacyEnabled(sensor, &result); @@ -143,6 +143,39 @@ status_t SensorPrivacyManager::isToggleSensorPrivacyEnabled(int toggleType, int return UNKNOWN_ERROR; } +int SensorPrivacyManager::getToggleSensorPrivacyState(int toggleType, int sensor) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + int result; + service->getToggleSensorPrivacyState(toggleType, sensor, &result); + return result; + } + // if the SensorPrivacyManager is not available then assume sensor privacy is disabled + return DISABLED; +} + +std::vector<hardware::CameraPrivacyAllowlistEntry> + SensorPrivacyManager::getCameraPrivacyAllowlist(){ + sp<hardware::ISensorPrivacyManager> service = getService(); + std::vector<hardware::CameraPrivacyAllowlistEntry> result; + if (service != nullptr) { + service->getCameraPrivacyAllowlist(&result); + return result; + } + return result; +} + +bool SensorPrivacyManager::isCameraPrivacyEnabled(String16 packageName){ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + bool result; + service->isCameraPrivacyEnabled(packageName, &result); + return result; + } + return false; +} + status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<hardware::ISensorPrivacyManager> service = getService(); diff --git a/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl b/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl new file mode 100644 index 0000000000..03e153704b --- /dev/null +++ b/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +parcelable CameraPrivacyAllowlistEntry { + String packageName; + boolean isMandatory; +} diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl index eccd54c3eb..694af00a87 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl @@ -21,4 +21,5 @@ package android.hardware; */ oneway interface ISensorPrivacyListener { void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled); + void onSensorPrivacyStateChanged(int toggleType, int sensor, int state); } diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl index 49a1e1ea05..b6bd39e557 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl @@ -16,6 +16,7 @@ package android.hardware; +import android.hardware.CameraPrivacyAllowlistEntry; import android.hardware.ISensorPrivacyListener; /** @hide */ @@ -41,4 +42,15 @@ interface ISensorPrivacyManager { void setToggleSensorPrivacy(int userId, int source, int sensor, boolean enable); void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable); + + List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist(); + + int getToggleSensorPrivacyState(int toggleType, int sensor); + + void setToggleSensorPrivacyState(int userId, int source, int sensor, int state); + + void setToggleSensorPrivacyStateForProfileGroup(int userId, int source, int sensor, int state); + + boolean isCameraPrivacyEnabled(String packageName); + } diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h index fc5fdf7900..9e97e166be 100644 --- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h @@ -32,12 +32,22 @@ class SensorPrivacyManager public: enum { TOGGLE_SENSOR_MICROPHONE = 1, - TOGGLE_SENSOR_CAMERA = 2 + TOGGLE_SENSOR_CAMERA = 2, + TOGGLE_SENSOR_UNKNOWN = -1 }; enum { TOGGLE_TYPE_SOFTWARE = 1, - TOGGLE_TYPE_HARDWARE = 2 + TOGGLE_TYPE_HARDWARE = 2, + TOGGLE_TYPE_UNKNOWN = -1 + }; + + enum { + ENABLED = 1, + DISABLED = 2, + AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS = 3, + AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS = 4, + AUTOMOTIVE_DRIVER_ASSISTANCE_APPS = 5 }; SensorPrivacyManager(); @@ -51,6 +61,9 @@ public: bool isToggleSensorPrivacyEnabled(int sensor); bool isToggleSensorPrivacyEnabled(int toggleType, int sensor); status_t isToggleSensorPrivacyEnabled(int toggleType, int sensor, bool &result); + int getToggleSensorPrivacyState(int toggleType, int sensor); + std::vector<hardware::CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist(); + bool isCameraPrivacyEnabled(String16 packageName); status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp index 960f845488..82fbfda0da 100644 --- a/libs/shaders/Android.bp +++ b/libs/shaders/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library_static { diff --git a/libs/shaders/tests/Android.bp b/libs/shaders/tests/Android.bp index 5639d744df..2103679efe 100644 --- a/libs/shaders/tests/Android.bp +++ b/libs/shaders/tests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp index 8c8815d0e4..d569ac3ba6 100644 --- a/libs/tonemap/Android.bp +++ b/libs/tonemap/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library_static { diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp index 5c5fc6c8b3..79ee7c2de9 100644 --- a/libs/tonemap/tests/Android.bp +++ b/libs/tonemap/tests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index f84d14548f..312a1e68b3 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["frameworks_native_libs_ui_license"], + default_team: "trendy_team_android_core_graphics_stack", } // Added automatically by a large-scale-change diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index e9b5decee8..a5aca9912f 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -384,8 +384,8 @@ std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const { status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t* outStride, - buffer_handle_t* outBufferHandles, bool importBuffers) const { + uint32_t* outStride, buffer_handle_t* outBufferHandles, + bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo = {}; descriptorInfo.width = width; descriptorInfo.height = height; @@ -400,6 +400,8 @@ status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t wid return error; } + constexpr auto bufferCount = 1; + auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp index 474d381dbb..152b35a505 100644 --- a/libs/ui/Gralloc3.cpp +++ b/libs/ui/Gralloc3.cpp @@ -371,7 +371,7 @@ std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const { status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); @@ -383,6 +383,8 @@ status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t wid return error; } + constexpr auto bufferCount = 1; + auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 03ff58a76c..2a607308d1 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -262,37 +262,8 @@ void Gralloc4Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* ou status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, void** outData, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) const { - std::vector<ui::PlaneLayout> planeLayouts; - status_t err = getPlaneLayouts(bufferHandle, &planeLayouts); - - if (err == NO_ERROR && !planeLayouts.empty()) { - if (outBytesPerPixel) { - int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits; - for (const auto& planeLayout : planeLayouts) { - if (bitsPerPixel != planeLayout.sampleIncrementInBits) { - bitsPerPixel = -1; - } - } - if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) { - *outBytesPerPixel = bitsPerPixel / 8; - } else { - *outBytesPerPixel = -1; - } - } - if (outBytesPerStride) { - int32_t bytesPerStride = planeLayouts.front().strideInBytes; - for (const auto& planeLayout : planeLayouts) { - if (bytesPerStride != planeLayout.strideInBytes) { - bytesPerStride = -1; - } - } - if (bytesPerStride >= 0) { - *outBytesPerStride = bytesPerStride; - } else { - *outBytesPerStride = -1; - } - } - } + if (outBytesPerPixel) *outBytesPerPixel = -1; + if (outBytesPerStride) *outBytesPerStride = -1; auto buffer = const_cast<native_handle_t*>(bufferHandle); @@ -1069,7 +1040,7 @@ std::string Gralloc4Allocator::dumpDebugInfo(bool less) const { status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, @@ -1084,6 +1055,8 @@ status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, return error; } + constexpr auto bufferCount = 1; + if (mAidlAllocator) { AllocationResult result; #pragma clang diagnostic push diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp index 303043a35a..f14a5cf1a2 100644 --- a/libs/ui/Gralloc5.cpp +++ b/libs/ui/Gralloc5.cpp @@ -19,6 +19,7 @@ #include <ui/Gralloc5.h> +#include <aidl/android/hardware/graphics/allocator/AllocationError.h> #include <aidlcommonsupport/NativeHandle.h> #include <android/binder_manager.h> #include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h> @@ -223,55 +224,75 @@ std::string Gralloc5Allocator::dumpDebugInfo(bool less) const { status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t *outStride, - buffer_handle_t *outBufferHandles, bool importBuffers) const { - auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage); + uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers) const { + auto result = allocate(GraphicBufferAllocator::AllocationRequest{ + .importBuffer = importBuffers, + .width = width, + .height = height, + .format = format, + .layerCount = layerCount, + .usage = usage, + .requestorName = requestorName, + }); + + *outStride = result.stride; + outBufferHandles[0] = result.handle; + return result.status; +} + +GraphicBufferAllocator::AllocationResult Gralloc5Allocator::allocate( + const GraphicBufferAllocator::AllocationRequest& request) const { + auto descriptorInfo = makeDescriptor(request.requestorName, request.width, request.height, + request.format, request.layerCount, request.usage); if (!descriptorInfo) { - return BAD_VALUE; + return GraphicBufferAllocator::AllocationResult{BAD_VALUE}; + } + + descriptorInfo->additionalOptions.reserve(request.extras.size()); + for (const auto& option : request.extras) { + ExtendableType type; + type.name = option.name; + type.value = option.value; + descriptorInfo->additionalOptions.push_back(std::move(type)); } AllocationResult result; - auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result); + auto status = mAllocator->allocate2(*descriptorInfo, 1, &result); if (!status.isOk()) { auto error = status.getExceptionCode(); if (error == EX_SERVICE_SPECIFIC) { - error = status.getServiceSpecificError(); - } - if (error == OK) { - error = UNKNOWN_ERROR; + switch (static_cast<AllocationError>(status.getServiceSpecificError())) { + case AllocationError::BAD_DESCRIPTOR: + error = BAD_VALUE; + break; + case AllocationError::NO_RESOURCES: + error = NO_MEMORY; + break; + default: + error = UNKNOWN_ERROR; + break; + } } - return error; + return GraphicBufferAllocator::AllocationResult{error}; } - if (importBuffers) { - for (uint32_t i = 0; i < bufferCount; i++) { - auto handle = makeFromAidl(result.buffers[i]); - auto error = mMapper.importBuffer(handle, &outBufferHandles[i]); - native_handle_delete(handle); - if (error != NO_ERROR) { - for (uint32_t j = 0; j < i; j++) { - mMapper.freeBuffer(outBufferHandles[j]); - outBufferHandles[j] = nullptr; - } - return error; - } + GraphicBufferAllocator::AllocationResult ret{OK}; + if (request.importBuffer) { + auto handle = makeFromAidl(result.buffers[0]); + auto error = mMapper.importBuffer(handle, &ret.handle); + native_handle_delete(handle); + if (error != NO_ERROR) { + return GraphicBufferAllocator::AllocationResult{error}; } } else { - for (uint32_t i = 0; i < bufferCount; i++) { - outBufferHandles[i] = dupFromAidl(result.buffers[i]); - if (!outBufferHandles[i]) { - for (uint32_t j = 0; j < i; j++) { - auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]); - native_handle_close(buffer); - native_handle_delete(buffer); - outBufferHandles[j] = nullptr; - } - return NO_MEMORY; - } + ret.handle = dupFromAidl(result.buffers[0]); + if (!ret.handle) { + return GraphicBufferAllocator::AllocationResult{NO_MEMORY}; } } - *outStride = result.stride; + ret.stride = result.stride; // Release all the resources held by AllocationResult (specifically any remaining FDs) result = {}; @@ -280,7 +301,7 @@ status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, // is marked apex_available (b/214400477) and libbinder isn't (which of course is correct) // IPCThreadState::self()->flushCommands(); - return OK; + return ret; } void Gralloc5Mapper::preload() { @@ -576,37 +597,8 @@ void Gralloc5Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t *ou status_t Gralloc5Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds, int acquireFence, void **outData, int32_t *outBytesPerPixel, int32_t *outBytesPerStride) const { - std::vector<ui::PlaneLayout> planeLayouts; - status_t err = getPlaneLayouts(bufferHandle, &planeLayouts); - - if (err == NO_ERROR && !planeLayouts.empty()) { - if (outBytesPerPixel) { - int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits; - for (const auto &planeLayout : planeLayouts) { - if (bitsPerPixel != planeLayout.sampleIncrementInBits) { - bitsPerPixel = -1; - } - } - if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) { - *outBytesPerPixel = bitsPerPixel / 8; - } else { - *outBytesPerPixel = -1; - } - } - if (outBytesPerStride) { - int32_t bytesPerStride = planeLayouts.front().strideInBytes; - for (const auto &planeLayout : planeLayouts) { - if (bytesPerStride != planeLayout.strideInBytes) { - bytesPerStride = -1; - } - } - if (bytesPerStride >= 0) { - *outBytesPerStride = bytesPerStride; - } else { - *outBytesPerStride = -1; - } - } - } + if (outBytesPerPixel) *outBytesPerPixel = -1; + if (outBytesPerStride) *outBytesPerStride = -1; auto status = mMapper->v5.lock(bufferHandle, usage, bounds, acquireFence, outData); diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 429760ffe0..ffb6cdb6ef 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -22,7 +22,7 @@ #include <cutils/atomic.h> #include <grallocusage/GrallocUsageConversion.h> - +#include <sync/sync.h> #include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> #include <utils/Trace.h> @@ -40,6 +40,38 @@ static uint64_t getUniqueId() { return id; } +static void resolveLegacyByteLayoutFromPlaneLayout(const std::vector<ui::PlaneLayout>& planeLayouts, + int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { + if (planeLayouts.empty()) return; + if (outBytesPerPixel) { + int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits; + for (const auto& planeLayout : planeLayouts) { + if (bitsPerPixel != planeLayout.sampleIncrementInBits) { + bitsPerPixel = -1; + } + } + if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) { + *outBytesPerPixel = bitsPerPixel / 8; + } else { + *outBytesPerPixel = -1; + } + } + if (outBytesPerStride) { + int32_t bytesPerStride = planeLayouts.front().strideInBytes; + for (const auto& planeLayout : planeLayouts) { + if (bytesPerStride != planeLayout.strideInBytes) { + bytesPerStride = -1; + } + } + if (bytesPerStride >= 0) { + *outBytesPerStride = bytesPerStride; + } else { + *outBytesPerStride = -1; + } + } +} + sp<GraphicBuffer> GraphicBuffer::from(ANativeWindowBuffer* anwb) { return static_cast<GraphicBuffer *>(anwb); } @@ -106,6 +138,26 @@ GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod m inUsage, inStride); } +GraphicBuffer::GraphicBuffer(const GraphicBufferAllocator::AllocationRequest& request) + : GraphicBuffer() { + GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); + auto result = allocator.allocate(request); + mInitCheck = result.status; + if (result.status == NO_ERROR) { + handle = result.handle; + stride = result.stride; + + mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); + + width = static_cast<int>(request.width); + height = static_cast<int>(request.height); + format = request.format; + layerCount = request.layerCount; + usage = request.usage; + usage_deprecated = int(usage); + } +} + GraphicBuffer::~GraphicBuffer() { ATRACE_CALL(); @@ -143,6 +195,10 @@ ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const const_cast<GraphicBuffer*>(this)); } +status_t GraphicBuffer::getDataspace(ui::Dataspace* outDataspace) const { + return mBufferMapper.getDataspace(handle, outDataspace); +} + status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage) { @@ -255,10 +311,7 @@ status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr, return BAD_VALUE; } - status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr, outBytesPerPixel, - outBytesPerStride); - - return res; + return lockAsync(inUsage, rect, vaddr, -1, outBytesPerPixel, outBytesPerStride); } status_t GraphicBuffer::lockYCbCr(uint32_t inUsage, android_ycbcr* ycbcr) @@ -278,14 +331,12 @@ status_t GraphicBuffer::lockYCbCr(uint32_t inUsage, const Rect& rect, width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lockYCbCr(handle, inUsage, rect, ycbcr); - return res; + return lockAsyncYCbCr(inUsage, rect, ycbcr, -1); } status_t GraphicBuffer::unlock() { - status_t res = getBufferMapper().unlock(handle); - return res; + return unlockAsync(nullptr); } status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd, @@ -312,10 +363,49 @@ status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerU return BAD_VALUE; } - status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect, - vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); + // Resolve the bpp & bps before doing a lock in case this fails we don't have to worry about + // doing an unlock + int32_t legacyBpp = -1, legacyBps = -1; + if (outBytesPerPixel || outBytesPerStride) { + const auto mapperVersion = getBufferMapperVersion(); + // For gralloc2 we need to guess at the bpp & bps + // For gralloc3 the lock() call will return it + // For gralloc4 & later the PlaneLayout metadata query is vastly superior and we + // resolve bpp & bps just for compatibility + + // TODO: See if we can't just remove gralloc2 support. + if (mapperVersion == GraphicBufferMapper::GRALLOC_2) { + legacyBpp = bytesPerPixel(format); + if (legacyBpp > 0) { + legacyBps = stride * legacyBpp; + } else { + legacyBpp = -1; + } + } else if (mapperVersion >= GraphicBufferMapper::GRALLOC_4) { + auto planeLayout = getBufferMapper().getPlaneLayouts(handle); + if (!planeLayout.has_value()) return planeLayout.asStatus(); + resolveLegacyByteLayoutFromPlaneLayout(planeLayout.value(), &legacyBpp, &legacyBps); + } + } - return res; + const uint64_t usage = static_cast<uint64_t>( + android_convertGralloc1To0Usage(inProducerUsage, inConsumerUsage)); + + auto result = getBufferMapper().lock(handle, usage, rect, base::unique_fd{fenceFd}); + + if (!result.has_value()) { + return result.error().asStatus(); + } + auto value = result.value(); + *vaddr = value.address; + + if (outBytesPerPixel) { + *outBytesPerPixel = legacyBpp != -1 ? legacyBpp : value.bytesPerPixel; + } + if (outBytesPerStride) { + *outBytesPerStride = legacyBps != -1 ? legacyBps : value.bytesPerStride; + } + return OK; } status_t GraphicBuffer::lockAsyncYCbCr(uint32_t inUsage, android_ycbcr* ycbcr, @@ -336,14 +426,18 @@ status_t GraphicBuffer::lockAsyncYCbCr(uint32_t inUsage, const Rect& rect, width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lockAsyncYCbCr(handle, inUsage, rect, ycbcr, fenceFd); - return res; + auto result = getBufferMapper().lockYCbCr(handle, static_cast<int64_t>(inUsage), rect, + base::unique_fd{fenceFd}); + if (!result.has_value()) { + return result.error().asStatus(); + } + *ycbcr = result.value(); + return OK; } status_t GraphicBuffer::unlockAsync(int *fenceFd) { - status_t res = getBufferMapper().unlockAsync(handle, fenceFd); - return res; + return getBufferMapper().unlockAsync(handle, fenceFd); } status_t GraphicBuffer::isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index eb0bd4ed0a..98082fb81e 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -113,6 +113,79 @@ void GraphicBufferAllocator::dumpToSystemLog(bool less) { ALOGD("%s", s.c_str()); } +auto GraphicBufferAllocator::allocate(const AllocationRequest& request) -> AllocationResult { + ATRACE_CALL(); + if (!request.width || !request.height) { + return AllocationResult(BAD_VALUE); + } + + const auto width = request.width; + const auto height = request.height; + + const uint32_t bpp = bytesPerPixel(request.format); + if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": Requesting too large a buffer size", + request.width, request.height, request.layerCount, request.format, request.usage); + return AllocationResult(BAD_VALUE); + } + + if (request.layerCount < 1) { + return AllocationResult(BAD_VALUE); + } + + auto result = mAllocator->allocate(request); + if (result.status == UNKNOWN_TRANSACTION) { + if (!request.extras.empty()) { + ALOGE("Failed to allocate with additional options, allocator version mis-match? " + "gralloc version = %d", + (int)mMapper.getMapperVersion()); + return result; + } + // If there's no additional options, fall back to previous allocate version + result.status = mAllocator->allocate(request.requestorName, request.width, request.height, + request.format, request.layerCount, request.usage, + &result.stride, &result.handle, request.importBuffer); + } + + if (result.status != NO_ERROR) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": %d", + request.width, request.height, request.layerCount, request.format, request.usage, + result.status); + return result; + } + + if (!request.importBuffer) { + return result; + } + size_t bufSize; + + // if stride has no meaning or is too large, + // approximate size with the input width instead + if ((result.stride) != 0 && + std::numeric_limits<size_t>::max() / height / (result.stride) < static_cast<size_t>(bpp)) { + bufSize = static_cast<size_t>(width) * height * bpp; + } else { + bufSize = static_cast<size_t>((result.stride)) * height * bpp; + } + + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + alloc_rec_t rec; + rec.width = width; + rec.height = height; + rec.stride = result.stride; + rec.format = request.format; + rec.layerCount = request.layerCount; + rec.usage = request.usage; + rec.size = bufSize; + rec.requestorName = request.requestorName; + list.add(result.handle, rec); + + return result; +} + status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride, @@ -141,7 +214,7 @@ status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage, - 1, stride, handle, importBuffer); + stride, handle, importBuffer); if (error != NO_ERROR) { ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " "usage %" PRIx64 ": %d", diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index dc026d2a57..b6ab2f5a47 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -41,9 +41,13 @@ #include <system/graphics.h> +using unique_fd = ::android::base::unique_fd; + namespace android { // --------------------------------------------------------------------------- +using LockResult = GraphicBufferMapper::LockResult; + ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) void GraphicBufferMapper::preloadHal() { @@ -135,63 +139,86 @@ status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle) return NO_ERROR; } -status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, - void** vaddr, int32_t* outBytesPerPixel, - int32_t* outBytesPerStride) { - return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride); -} - -status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage, - const Rect& bounds, android_ycbcr *ycbcr) -{ - return lockAsyncYCbCr(handle, usage, bounds, ycbcr, -1); -} +ui::Result<LockResult> GraphicBufferMapper::lock(buffer_handle_t handle, int64_t usage, + const Rect& bounds, unique_fd&& acquireFence) { + ATRACE_CALL(); -status_t GraphicBufferMapper::unlock(buffer_handle_t handle) -{ - int32_t fenceFd = -1; - status_t error = unlockAsync(handle, &fenceFd); - if (error == NO_ERROR && fenceFd >= 0) { - sync_wait(fenceFd, -1); - close(fenceFd); + LockResult result; + status_t status = mMapper->lock(handle, usage, bounds, acquireFence.release(), &result.address, + &result.bytesPerPixel, &result.bytesPerStride); + if (status != OK) { + return base::unexpected(ui::Error::statusToCode(status)); + } else { + return result; } - return error; -} - -status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, - void** vaddr, int fenceFd, int32_t* outBytesPerPixel, - int32_t* outBytesPerStride) { - return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel, - outBytesPerStride); } -status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, - uint64_t consumerUsage, const Rect& bounds, void** vaddr, - int fenceFd, int32_t* outBytesPerPixel, - int32_t* outBytesPerStride) { +ui::Result<android_ycbcr> GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, int64_t usage, + const Rect& bounds, + base::unique_fd&& acquireFence) { ATRACE_CALL(); - const uint64_t usage = static_cast<uint64_t>(ANDROID_NATIVE_UNSIGNED_CAST( - android_convertGralloc1To0Usage(producerUsage, consumerUsage))); - return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel, - outBytesPerStride); + android_ycbcr result = {}; + status_t status = mMapper->lock(handle, usage, bounds, acquireFence.release(), &result); + if (status != OK) { + return base::unexpected(ui::Error::statusToCode(status)); + } else { + return result; + } } -status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd) -{ +status_t GraphicBufferMapper::unlock(buffer_handle_t handle, base::unique_fd* outFence) { ATRACE_CALL(); + int fence = mMapper->unlock(handle); + if (outFence) { + *outFence = unique_fd{fence}; + } else { + sync_wait(fence, -1); + close(fence); + } + return OK; +} - return mMapper->lock(handle, usage, bounds, fenceFd, ycbcr); +status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + void** vaddr) { + auto result = lock(handle, static_cast<int64_t>(usage), bounds); + if (!result.has_value()) return result.asStatus(); + auto val = result.value(); + *vaddr = val.address; + return OK; } -status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd) -{ - ATRACE_CALL(); +status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + android_ycbcr* ycbcr) { + auto result = lockYCbCr(handle, static_cast<int64_t>(usage), bounds); + if (!result.has_value()) return result.asStatus(); + *ycbcr = result.value(); + return OK; +} + +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + void** vaddr, int fenceFd) { + auto result = lock(handle, static_cast<int64_t>(usage), bounds, unique_fd{fenceFd}); + if (!result.has_value()) return result.asStatus(); + auto val = result.value(); + *vaddr = val.address; + return OK; +} - *fenceFd = mMapper->unlock(handle); +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, + uint64_t consumerUsage, const Rect& bounds, void** vaddr, + int fenceFd) { + return lockAsync(handle, android_convertGralloc1To0Usage(producerUsage, consumerUsage), bounds, + vaddr, fenceFd); +} - return NO_ERROR; +status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, + const Rect& bounds, android_ycbcr* ycbcr, + int fenceFd) { + auto result = lockYCbCr(handle, static_cast<int64_t>(usage), bounds, unique_fd{fenceFd}); + if (!result.has_value()) return result.asStatus(); + *ycbcr = result.value(); + return OK; } status_t GraphicBufferMapper::isSupported(uint32_t width, uint32_t height, @@ -287,6 +314,17 @@ status_t GraphicBufferMapper::getPlaneLayouts(buffer_handle_t bufferHandle, return mMapper->getPlaneLayouts(bufferHandle, outPlaneLayouts); } +ui::Result<std::vector<ui::PlaneLayout>> GraphicBufferMapper::getPlaneLayouts( + buffer_handle_t bufferHandle) { + std::vector<ui::PlaneLayout> temp; + status_t status = mMapper->getPlaneLayouts(bufferHandle, &temp); + if (status == OK) { + return std::move(temp); + } else { + return base::unexpected(ui::Error::statusToCode(status)); + } +} + status_t GraphicBufferMapper::getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) { return mMapper->getDataspace(bufferHandle, outDataspace); diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h index 496ba57789..e6015e0b5e 100644 --- a/libs/ui/include/ui/Gralloc.h +++ b/libs/ui/include/ui/Gralloc.h @@ -23,6 +23,7 @@ #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <utils/StrongPointer.h> +#include "GraphicBufferAllocator.h" #include <string> @@ -218,9 +219,13 @@ public: */ virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t* outStride, - buffer_handle_t* outBufferHandles, + uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers = true) const = 0; + + virtual GraphicBufferAllocator::AllocationResult allocate( + const GraphicBufferAllocator::AllocationRequest&) const { + return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION); + } }; } // namespace android diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h index a7b6f49206..e50bb3af3e 100644 --- a/libs/ui/include/ui/Gralloc2.h +++ b/libs/ui/include/ui/Gralloc2.h @@ -81,9 +81,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc2Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h index 7367549964..035684abcd 100644 --- a/libs/ui/include/ui/Gralloc3.h +++ b/libs/ui/include/ui/Gralloc3.h @@ -82,9 +82,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc3Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h index df43be87cd..0f469c0b7e 100644 --- a/libs/ui/include/ui/Gralloc4.h +++ b/libs/ui/include/ui/Gralloc4.h @@ -174,9 +174,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc4Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h index 44b97d1a6f..f9e8f5e9fd 100644 --- a/libs/ui/include/ui/Gralloc5.h +++ b/libs/ui/include/ui/Gralloc5.h @@ -172,10 +172,12 @@ public: [[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t *outStride, - buffer_handle_t *outBufferHandles, + uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const override; + [[nodiscard]] GraphicBufferAllocator::AllocationResult allocate( + const GraphicBufferAllocator::AllocationRequest&) const override; + private: const Gralloc5Mapper &mMapper; std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator; diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index f859848b0c..652d8ba709 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -26,6 +26,7 @@ #include <android/hardware_buffer.h> #include <ui/ANativeObjectBase.h> +#include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> @@ -103,6 +104,8 @@ public: uint32_t inLayerCount, uint64_t inUsage, std::string requestorName = "<Unknown>"); + GraphicBuffer(const GraphicBufferAllocator::AllocationRequest&); + // Create a GraphicBuffer from an existing handle. enum HandleWrapMethod : uint8_t { // Wrap and use the handle directly. It assumes the handle has been @@ -169,6 +172,8 @@ public: mGenerationNumber = generation; } + status_t getDataspace(ui::Dataspace* outDataspace) const; + // This function is privileged. It requires access to the allocator // device or service, which usually involves adding suitable selinux // rules. diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 3ed988c4c2..8f461e193b 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -22,6 +22,7 @@ #include <memory> #include <string> +#include <vector> #include <cutils/native_handle.h> @@ -42,6 +43,35 @@ class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> public: static inline GraphicBufferAllocator& get() { return getInstance(); } + struct AdditionalOptions { + const char* name; + int64_t value; + }; + + struct AllocationRequest { + bool importBuffer; + uint32_t width; + uint32_t height; + PixelFormat format; + uint32_t layerCount; + uint64_t usage; + std::string requestorName; + std::vector<AdditionalOptions> extras; + }; + + struct AllocationResult { + status_t status; + buffer_handle_t handle = nullptr; + uint32_t stride = 0; + + explicit AllocationResult(status_t status) : status(status) {} + + explicit AllocationResult(buffer_handle_t handle, uint32_t stride) + : status(OK), handle(handle), stride(stride) {} + }; + + AllocationResult allocate(const AllocationRequest&); + /** * Allocates and imports a gralloc buffer. * diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h index 3a5167ab25..9da1447aed 100644 --- a/libs/ui/include/ui/GraphicBufferMapper.h +++ b/libs/ui/include/ui/GraphicBufferMapper.h @@ -22,9 +22,11 @@ #include <memory> +#include <android-base/unique_fd.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> +#include <ui/Result.h> #include <utils/Singleton.h> // Needed by code that still uses the GRALLOC_USAGE_* constants. @@ -38,6 +40,12 @@ namespace android { class GrallocMapper; +/** + * This class is a thin wrapper over the various gralloc HALs. It is a "raw" wrapper, having + * version-specific behaviors & features. It is not recommend for general use. It is instead + * strongly recommended to use AHardwareBuffer or ui::GraphicBuffer which will provide stronger + * API compatibility & consistency behaviors. + */ class GraphicBufferMapper : public Singleton<GraphicBufferMapper> { public: @@ -66,27 +74,50 @@ public: void getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts); - status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, - int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); + struct LockResult { + void* address = nullptr; + /** + * Note: bytesPerPixel is only populated if version is gralloc 3 + * Gralloc 4 & later should use instead getPlaneLayout() + */ + int32_t bytesPerPixel = -1; + /** + * Note: bytesPerPixel is only populated if version is gralloc 3 + * Gralloc 4 & later should use instead getPlaneLayout() + */ + int32_t bytesPerStride = -1; + }; + + ui::Result<LockResult> lock(buffer_handle_t handle, int64_t usage, const Rect& bounds, + base::unique_fd&& acquireFence = {}); + + ui::Result<android_ycbcr> lockYCbCr(buffer_handle_t handle, int64_t usage, const Rect& bounds, + base::unique_fd&& acquireFence = {}); + + status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr); status_t lockYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr); - status_t unlock(buffer_handle_t handle); - status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, - int fenceFd, int32_t* outBytesPerPixel = nullptr, - int32_t* outBytesPerStride = nullptr); + int fenceFd); status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, - const Rect& bounds, void** vaddr, int fenceFd, - int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); + const Rect& bounds, void** vaddr, int fenceFd); status_t lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd); - status_t unlockAsync(buffer_handle_t handle, int *fenceFd); + status_t unlock(buffer_handle_t handle, base::unique_fd* outFence = nullptr); + status_t unlockAsync(buffer_handle_t handle, int* fenceFd) { + base::unique_fd temp; + status_t result = unlock(handle, fenceFd ? &temp : nullptr); + if (fenceFd) { + *fenceFd = temp.release(); + } + return result; + } status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, bool* outSupported); @@ -122,6 +153,7 @@ public: status_t getChromaSiting(buffer_handle_t bufferHandle, ui::ChromaSiting* outChromaSiting); status_t getPlaneLayouts(buffer_handle_t bufferHandle, std::vector<ui::PlaneLayout>* outPlaneLayouts); + ui::Result<std::vector<ui::PlaneLayout>> getPlaneLayouts(buffer_handle_t bufferHandle); status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace); status_t setDataspace(buffer_handle_t bufferHandle, ui::Dataspace dataspace); status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode); diff --git a/libs/ui/include/ui/Result.h b/libs/ui/include/ui/Result.h new file mode 100644 index 0000000000..d73c3e23b7 --- /dev/null +++ b/libs/ui/include/ui/Result.h @@ -0,0 +1,121 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/expected.h> +#include <utils/Errors.h> + +namespace android::ui { + +enum class ErrorCode : int32_t { + /** + * No error. + */ + None = 0, + /** + * Invalid BufferDescriptor. + */ + BadDescriptor = 1, + /** + * Invalid buffer handle. + */ + BadBuffer = 2, + /** + * Invalid HardwareBufferDescription. + */ + BadValue = 3, + /** + * Resource unavailable. + */ + NoResources = 5, + /** + * Permanent failure. + */ + Unsupported = 7, +}; + +class Error { +public: + Error(ErrorCode err) : mCode(err) {} + + Error(ErrorCode err, std::string&& message) : mCode(err), mMessage(std::move(message)) {} + Error(ErrorCode err, const std::string_view& message) : mCode(err), mMessage(message) {} + + static constexpr status_t codeToStatus(ErrorCode code) { + switch (code) { + case ErrorCode::None: + return OK; + case ErrorCode::BadDescriptor: + return BAD_VALUE; + case ErrorCode::BadValue: + return BAD_VALUE; + case ErrorCode::BadBuffer: + return BAD_TYPE; + case ErrorCode::NoResources: + return NO_MEMORY; + case ErrorCode::Unsupported: + return INVALID_OPERATION; + default: + return UNKNOWN_ERROR; + } + } + + static constexpr ErrorCode statusToCode(status_t status) { + switch (status) { + case OK: + return ErrorCode::None; + case BAD_VALUE: + return ErrorCode::BadValue; + case BAD_TYPE: + return ErrorCode::BadBuffer; + case NO_MEMORY: + return ErrorCode::NoResources; + case INVALID_OPERATION: + return ErrorCode::Unsupported; + default: + return ErrorCode::Unsupported; + } + } + + constexpr status_t asStatus() const { return codeToStatus(mCode); } + + ErrorCode code() const { return mCode; } + + const std::string& message() const { return mMessage; } + + bool operator==(const ErrorCode code) { return mCode == code; } + +private: + ErrorCode mCode; + std::string mMessage; +}; + +template <typename T> +class Result : public base::expected<T, Error> { +public: + using base::expected<T, Error>::expected; + + [[nodiscard]] constexpr status_t asStatus() const { + return this->has_value() ? OK : this->error().asStatus(); + } + + [[nodiscard]] constexpr ErrorCode errorCode() const { + return this->has_value() ? ErrorCode::None : this->error().code(); + } +}; + +} // namespace android::ui diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 8ce017d7a3..2d8a1e326c 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -21,6 +21,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_libs_ui_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { @@ -54,6 +55,17 @@ cc_test { } cc_test { + name: "DisplayIdentification_test", + shared_libs: ["libui"], + static_libs: ["libgmock"], + srcs: ["DisplayIdentification_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], +} + +cc_test { name: "FlattenableHelpers_test", shared_libs: ["libui"], srcs: ["FlattenableHelpers_test.cpp"], diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp index f4c0afa71b..efca083e6e 100644 --- a/libs/ui/tests/GraphicBufferAllocator_test.cpp +++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp @@ -51,7 +51,7 @@ public: std::cout << "Setting expected stride to " << stride << std::endl; EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())), allocate) - .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err))); + .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err))); } std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; } }; diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h index d62e3e2192..d02b3873e0 100644 --- a/libs/ui/tests/mock/MockGrallocAllocator.h +++ b/libs/ui/tests/mock/MockGrallocAllocator.h @@ -35,7 +35,7 @@ public: MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override)); MOCK_METHOD(status_t, allocate, (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, - uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint32_t layerCount, uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool less), (const, override)); }; diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp index 5d6070cf95..cc233b917b 100644 --- a/libs/ui/tools/Android.bp +++ b/libs/ui/tools/Android.bp @@ -21,6 +21,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_libs_ui_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp index 80e911c65d..c97e496083 100644 --- a/libs/vibrator/ExternalVibration.cpp +++ b/libs/vibrator/ExternalVibration.cpp @@ -17,7 +17,7 @@ #include <vibrator/ExternalVibration.h> #include <vibrator/ExternalVibrationUtils.h> -#include <android/os/IExternalVibratorService.h> +#include <android/os/ExternalVibrationScale.h> #include <binder/Parcel.h> #include <log/log.h> #include <utils/Errors.h> @@ -65,24 +65,36 @@ inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const { return mToken == rhs.mToken; } -os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(int externalVibrationScale) { - switch (externalVibrationScale) { - case IExternalVibratorService::SCALE_MUTE: - return os::HapticScale::MUTE; - case IExternalVibratorService::SCALE_VERY_LOW: - return os::HapticScale::VERY_LOW; - case IExternalVibratorService::SCALE_LOW: - return os::HapticScale::LOW; - case IExternalVibratorService::SCALE_NONE: - return os::HapticScale::NONE; - case IExternalVibratorService::SCALE_HIGH: - return os::HapticScale::HIGH; - case IExternalVibratorService::SCALE_VERY_HIGH: - return os::HapticScale::VERY_HIGH; +os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale( + os::ExternalVibrationScale externalVibrationScale) { + os::HapticLevel scaleLevel = os::HapticLevel::NONE; + + switch (externalVibrationScale.scaleLevel) { + case os::ExternalVibrationScale::ScaleLevel::SCALE_MUTE: + scaleLevel = os::HapticLevel::MUTE; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_VERY_LOW: + scaleLevel = os::HapticLevel::VERY_LOW; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_LOW: + scaleLevel = os::HapticLevel::LOW; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_NONE: + scaleLevel = os::HapticLevel::NONE; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_HIGH: + scaleLevel = os::HapticLevel::HIGH; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_VERY_HIGH: + scaleLevel = os::HapticLevel::VERY_HIGH; + break; default: - ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", externalVibrationScale); - return os::HapticScale::NONE; - } + ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", + externalVibrationScale.scaleLevel); + } + + return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/ + externalVibrationScale.adaptiveHapticsScale}; } } // namespace os diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp index 980b08ba36..761ac1bf3b 100644 --- a/libs/vibrator/ExternalVibrationUtils.cpp +++ b/libs/vibrator/ExternalVibrationUtils.cpp @@ -26,30 +26,30 @@ static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f; static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f; static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; -float getHapticScaleGamma(HapticScale scale) { - switch (scale) { - case HapticScale::VERY_LOW: +float getHapticScaleGamma(HapticLevel level) { + switch (level) { + case HapticLevel::VERY_LOW: return 2.0f; - case HapticScale::LOW: + case HapticLevel::LOW: return 1.5f; - case HapticScale::HIGH: + case HapticLevel::HIGH: return 0.5f; - case HapticScale::VERY_HIGH: + case HapticLevel::VERY_HIGH: return 0.25f; default: return 1.0f; } } -float getHapticMaxAmplitudeRatio(HapticScale scale) { - switch (scale) { - case HapticScale::VERY_LOW: +float getHapticMaxAmplitudeRatio(HapticLevel level) { + switch (level) { + case HapticLevel::VERY_LOW: return HAPTIC_SCALE_VERY_LOW_RATIO; - case HapticScale::LOW: + case HapticLevel::LOW: return HAPTIC_SCALE_LOW_RATIO; - case HapticScale::NONE: - case HapticScale::HIGH: - case HapticScale::VERY_HIGH: + case HapticLevel::NONE: + case HapticLevel::HIGH: + case HapticLevel::VERY_HIGH: return 1.0f; default: return 0.0f; @@ -57,19 +57,28 @@ float getHapticMaxAmplitudeRatio(HapticScale scale) { } void applyHapticScale(float* buffer, size_t length, HapticScale scale) { - if (scale == HapticScale::MUTE) { + if (scale.isScaleMute()) { memset(buffer, 0, length * sizeof(float)); return; } - if (scale == HapticScale::NONE) { + if (scale.isScaleNone()) { return; } - float gamma = getHapticScaleGamma(scale); - float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale); + HapticLevel hapticLevel = scale.getLevel(); + float adaptiveScaleFactor = scale.getAdaptiveScaleFactor(); + float gamma = getHapticScaleGamma(hapticLevel); + float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel); + for (size_t i = 0; i < length; i++) { - float sign = buffer[i] >= 0 ? 1.0 : -1.0; - buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) - * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + if (hapticLevel != HapticLevel::NONE) { + float sign = buffer[i] >= 0 ? 1.0 : -1.0; + buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + } + + if (adaptiveScaleFactor != 1.0f) { + buffer[i] *= adaptiveScaleFactor; + } } } @@ -89,13 +98,13 @@ void clipHapticData(float* buffer, size_t length, float limit) { } // namespace bool isValidHapticScale(HapticScale scale) { - switch (scale) { - case HapticScale::MUTE: - case HapticScale::VERY_LOW: - case HapticScale::LOW: - case HapticScale::NONE: - case HapticScale::HIGH: - case HapticScale::VERY_HIGH: + switch (scale.getLevel()) { + case HapticLevel::MUTE: + case HapticLevel::VERY_LOW: + case HapticLevel::LOW: + case HapticLevel::NONE: + case HapticLevel::HIGH: + case HapticLevel::VERY_HIGH: return true; } return false; diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h index 00cd3cd256..ac2767e17e 100644 --- a/libs/vibrator/include/vibrator/ExternalVibration.h +++ b/libs/vibrator/include/vibrator/ExternalVibration.h @@ -24,6 +24,7 @@ #include <system/audio.h> #include <utils/RefBase.h> #include <vibrator/ExternalVibrationUtils.h> +#include <android/os/ExternalVibrationScale.h> namespace android { namespace os { @@ -45,10 +46,11 @@ public : audio_attributes_t getAudioAttributes() const { return mAttrs; } sp<IExternalVibrationController> getController() { return mController; } - /* Converts the scale from non-public ExternalVibrationService into the HapticScale + /* Converts the scale from non-public ExternalVibrationService into the HapticScaleLevel * used by the utils. */ - static os::HapticScale externalVibrationScaleToHapticScale(int externalVibrationScale); + static os::HapticScale externalVibrationScaleToHapticScale( + os::ExternalVibrationScale externalVibrationScale); private: int32_t mUid; diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h index ca219d3cbf..d9a2b81ddf 100644 --- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -19,7 +19,7 @@ namespace android::os { -enum class HapticScale { +enum class HapticLevel { MUTE = -100, VERY_LOW = -2, LOW = -1, @@ -28,10 +28,41 @@ enum class HapticScale { VERY_HIGH = 2, }; +class HapticScale { +private: +HapticLevel mLevel = HapticLevel::NONE; +float mAdaptiveScaleFactor = 1.0f; + +public: +constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor) + : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {} +constexpr HapticScale(HapticLevel level) : mLevel(level) {} +constexpr HapticScale() {} + +HapticLevel getLevel() const { return mLevel; } +float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; } + +bool operator==(const HapticScale& other) const { + return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor; +} + +bool isScaleNone() const { + return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f; +} + +bool isScaleMute() const { + return mLevel == HapticLevel::MUTE; +} + +static HapticScale mute() { + return {/*level=*/os::HapticLevel::MUTE}; +} +}; + bool isValidHapticScale(HapticScale scale); -/* Scales the haptic data in given buffer using the selected HapticScale and ensuring no absolute - * value will be larger than the absolute of given limit. +/* Scales the haptic data in given buffer using the selected HapticScaleLevel and ensuring no + * absolute value will be larger than the absolute of given limit. * The limit will be ignored if it is NaN or zero. */ void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit); diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index e5b1e44b44..ca9fe5ea43 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -23,10 +23,11 @@ cc_defaults { name: "libgpuservice_defaults", defaults: [ "gpuservice_defaults", - "libvkjson_deps", "libgfxstats_deps", "libgpumem_deps", "libgpumemtracer_deps", + "libvkjson_deps", + "libvkprofiles_deps", ], cflags: [ "-DLOG_TAG=\"GpuService\"", @@ -46,6 +47,7 @@ cc_defaults { "libgpumemtracer", "libserviceutils", "libvkjson", + "libvkprofiles", ], export_static_lib_headers: [ "libserviceutils", diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index 080e62b164..a481c628aa 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -33,6 +33,7 @@ #include <utils/String8.h> #include <utils/Trace.h> #include <vkjson.h> +#include <vkprofiles.h> #include <thread> #include <memory> @@ -44,6 +45,7 @@ using base::StringAppendF; namespace { status_t cmdHelp(int out); status_t cmdVkjson(int out, int err); +status_t cmdVkprofiles(int out, int err); void dumpGameDriverInfo(std::string* result); } // namespace @@ -150,6 +152,7 @@ status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<Stri if (args.size() >= 1) { if (args[0] == String16("vkjson")) return cmdVkjson(out, err); + if (args[0] == String16("vkprofiles")) return cmdVkprofiles(out, err); if (args[0] == String16("help")) return cmdHelp(out); } // no command, or unrecognized command @@ -216,31 +219,24 @@ namespace { status_t cmdHelp(int out) { FILE* outs = fdopen(out, "w"); if (!outs) { - ALOGE("vkjson: failed to create out stream: %s (%d)", strerror(errno), errno); + ALOGE("gpuservice: failed to create out stream: %s (%d)", strerror(errno), errno); return BAD_VALUE; } fprintf(outs, "GPU Service commands:\n" - " vkjson dump Vulkan properties as JSON\n"); + " vkjson dump Vulkan properties as JSON\n" + " vkprofiles print support for select Vulkan profiles\n"); fclose(outs); return NO_ERROR; } -void vkjsonPrint(FILE* out) { - std::string json = VkJsonInstanceToJson(VkJsonGetInstance()); - fwrite(json.data(), 1, json.size(), out); - fputc('\n', out); +status_t cmdVkjson(int out, int /*err*/) { + dprintf(out, "%s\n", VkJsonInstanceToJson(VkJsonGetInstance()).c_str()); + return NO_ERROR; } -status_t cmdVkjson(int out, int /*err*/) { - FILE* outs = fdopen(out, "w"); - if (!outs) { - int errnum = errno; - ALOGE("vkjson: failed to create output stream: %s", strerror(errnum)); - return -errnum; - } - vkjsonPrint(outs); - fclose(outs); +status_t cmdVkprofiles(int out, int /*err*/) { + dprintf(out, "%s\n", android::vkprofiles::vkProfiles().c_str()); return NO_ERROR; } diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 7f2d03d310..d244b1abc4 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -14,6 +14,7 @@ // Default flags to be used throughout all libraries in inputflinger. package { + default_team: "trendy_team_input_framework", // 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" @@ -77,6 +78,7 @@ filegroup { "InputCommonConverter.cpp", "InputDeviceMetricsCollector.cpp", "InputFilter.cpp", + "InputFilterCallbacks.cpp", "InputProcessor.cpp", "PointerChoreographer.cpp", "PreferStylusOverTouchBlocker.cpp", @@ -109,6 +111,7 @@ cc_defaults { ], static_libs: [ "libattestation", + "libperfetto_client_experimental", "libpalmrejection", "libui-types", ], diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp index 9c4a3eb274..1ada5e5678 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -44,45 +44,25 @@ AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) { return event; } -NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) { - return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, - static_cast<uint32_t>(event.source), event.displayId, event.policyFlags, - static_cast<int32_t>(event.action), event.flags, event.keyCode, - event.scanCode, event.metaState, event.downTime); -} - -namespace { - -class RustCallbacks : public IInputFilter::BnInputFilterCallbacks { -public: - RustCallbacks(InputListenerInterface& nextListener) : mNextListener(nextListener) {} - ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override { - mNextListener.notifyKey(keyEventToNotifyKeyArgs(event)); - return ndk::ScopedAStatus::ok(); - } - -private: - InputListenerInterface& mNextListener; -}; - -} // namespace - -InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust) - : mNextListener(listener), mCallbacks(ndk::SharedRefBase::make<RustCallbacks>(listener)) { +InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust, + InputFilterPolicyInterface& policy) + : mNextListener(listener), + mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener, policy)), + mPolicy(policy) { LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk()); LOG_ALWAYS_FATAL_IF(!mInputFilterRust); } void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) { + mDeviceInfos.clear(); + mDeviceInfos.reserve(args.inputDeviceInfos.size()); + for (auto info : args.inputDeviceInfos) { + AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back(); + aidlInfo.deviceId = info.getId(); + aidlInfo.external = info.isExternal(); + } if (isFilterEnabled()) { - std::vector<AidlDeviceInfo> deviceInfos; - for (auto info : args.inputDeviceInfos) { - AidlDeviceInfo aidlInfo; - aidlInfo.deviceId = info.getId(); - aidlInfo.external = info.isExternal(); - deviceInfos.push_back(aidlInfo); - } - LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceInfos).isOk()); + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk()); } mNextListener.notify(args); } @@ -92,11 +72,11 @@ void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArg } void InputFilter::notifyKey(const NotifyKeyArgs& args) { - if (!isFilterEnabled()) { - mNextListener.notifyKey(args); + if (isFilterEnabled()) { + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk()); return; } - LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk()); + mNextListener.notify(args); } void InputFilter::notifyMotion(const NotifyMotionArgs& args) { @@ -134,7 +114,36 @@ void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) { if (mConfig.bounceKeysThresholdNs != threshold) { mConfig.bounceKeysThresholdNs = threshold; - LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk()); + notifyConfigurationChangedLocked(); + } +} + +void InputFilter::setAccessibilitySlowKeysThreshold(nsecs_t threshold) { + std::scoped_lock _l(mLock); + + if (mConfig.slowKeysThresholdNs != threshold) { + mConfig.slowKeysThresholdNs = threshold; + notifyConfigurationChangedLocked(); + } +} + +void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) { + std::scoped_lock _l(mLock); + + if (mConfig.stickyKeysEnabled != enabled) { + mConfig.stickyKeysEnabled = enabled; + notifyConfigurationChangedLocked(); + if (!enabled) { + // When Sticky keys is disabled, send callback to clear any saved sticky state. + mPolicy.notifyStickyModifierStateChanged(0, 0); + } + } +} + +void InputFilter::notifyConfigurationChangedLocked() { + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk()); + if (isFilterEnabled()) { + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk()); } } diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h index 06f7d0e601..4ddc9f4f6b 100644 --- a/services/inputflinger/InputFilter.h +++ b/services/inputflinger/InputFilter.h @@ -18,6 +18,8 @@ #include <aidl/com/android/server/inputflinger/IInputFlingerRust.h> #include <utils/Mutex.h> +#include "InputFilterCallbacks.h" +#include "InputFilterPolicyInterface.h" #include "InputListener.h" #include "NotifyArgs.h" @@ -33,6 +35,8 @@ public: */ virtual void dump(std::string& dump) = 0; virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0; + virtual void setAccessibilitySlowKeysThreshold(nsecs_t threshold) = 0; + virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0; }; class InputFilter : public InputFilterInterface { @@ -43,8 +47,10 @@ public: aidl::com::android::server::inputflinger::IInputFilter::IInputFilterCallbacks; using InputFilterConfiguration = aidl::com::android::server::inputflinger::InputFilterConfiguration; + using AidlDeviceInfo = aidl::com::android::server::inputflinger::DeviceInfo; - explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust&); + explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust, + InputFilterPolicyInterface& policy); ~InputFilter() override = default; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; @@ -56,16 +62,23 @@ public: void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override; + void setAccessibilitySlowKeysThreshold(nsecs_t threshold) override; + void setAccessibilityStickyKeysEnabled(bool enabled) override; void dump(std::string& dump) override; private: InputListenerInterface& mNextListener; - std::shared_ptr<IInputFilterCallbacks> mCallbacks; + std::shared_ptr<InputFilterCallbacks> mCallbacks; + InputFilterPolicyInterface& mPolicy; std::shared_ptr<IInputFilter> mInputFilterRust; + // Keep track of connected peripherals, so that if filters are enabled later, we can pass that + // info to the filters + std::vector<AidlDeviceInfo> mDeviceInfos; mutable std::mutex mLock; InputFilterConfiguration mConfig GUARDED_BY(mLock); bool isFilterEnabled(); + void notifyConfigurationChangedLocked() REQUIRES(mLock); }; } // namespace android diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp new file mode 100644 index 0000000000..6c3144230f --- /dev/null +++ b/services/inputflinger/InputFilterCallbacks.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputFilterCallbacks" + +#include "InputFilterCallbacks.h" +#include <aidl/com/android/server/inputflinger/BnInputThread.h> +#include <android/binder_auto_utils.h> +#include <utils/StrongPointer.h> +#include <utils/Thread.h> +#include <functional> + +namespace android { + +using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent; + +NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) { + return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, + static_cast<uint32_t>(event.source), event.displayId, event.policyFlags, + static_cast<int32_t>(event.action), event.flags, event.keyCode, + event.scanCode, event.metaState, event.downTime); +} + +namespace { + +using namespace aidl::com::android::server::inputflinger; + +class InputFilterThreadImpl : public Thread { +public: + explicit InputFilterThreadImpl(std::function<void()> loop) + : Thread(/*canCallJava=*/true), mThreadLoop(loop) {} + + ~InputFilterThreadImpl() {} + +private: + std::function<void()> mThreadLoop; + + bool threadLoop() override { + mThreadLoop(); + return true; + } +}; + +class InputFilterThread : public BnInputThread { +public: + InputFilterThread(std::shared_ptr<IInputThreadCallback> callback) : mCallback(callback) { + mThread = sp<InputFilterThreadImpl>::make([this]() { loopOnce(); }); + mThread->run("InputFilterThread", ANDROID_PRIORITY_URGENT_DISPLAY); + } + + ndk::ScopedAStatus finish() override { + mThread->requestExit(); + return ndk::ScopedAStatus::ok(); + } + +private: + sp<Thread> mThread; + std::shared_ptr<IInputThreadCallback> mCallback; + + void loopOnce() { LOG_ALWAYS_FATAL_IF(!mCallback->loopOnce().isOk()); } +}; + +} // namespace + +InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener, + InputFilterPolicyInterface& policy) + : mNextListener(listener), mPolicy(policy) {} + +ndk::ScopedAStatus InputFilterCallbacks::sendKeyEvent(const AidlKeyEvent& event) { + mNextListener.notifyKey(keyEventToNotifyKeyArgs(event)); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifierState, + int32_t lockedModifierState) { + std::scoped_lock _l(mLock); + mStickyModifierState.modifierState = modifierState; + mStickyModifierState.lockedModifierState = lockedModifierState; + mPolicy.notifyStickyModifierStateChanged(modifierState, lockedModifierState); + ALOGI("Sticky keys modifier state changed: modifierState=%d, lockedModifierState=%d", + modifierState, lockedModifierState); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus InputFilterCallbacks::createInputFilterThread( + const std::shared_ptr<IInputThreadCallback>& callback, + std::shared_ptr<IInputThread>* aidl_return) { + *aidl_return = ndk::SharedRefBase::make<InputFilterThread>(callback); + return ndk::ScopedAStatus::ok(); +} + +uint32_t InputFilterCallbacks::getModifierState() { + std::scoped_lock _l(mLock); + return mStickyModifierState.modifierState; +} + +uint32_t InputFilterCallbacks::getLockedModifierState() { + std::scoped_lock _l(mLock); + return mStickyModifierState.lockedModifierState; +} + +} // namespace android diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h new file mode 100644 index 0000000000..a74955b5c6 --- /dev/null +++ b/services/inputflinger/InputFilterCallbacks.h @@ -0,0 +1,65 @@ +/* + * Copyright 2023 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 <aidl/com/android/server/inputflinger/IInputFlingerRust.h> +#include <android/binder_auto_utils.h> +#include <utils/Mutex.h> +#include <memory> +#include <mutex> +#include "InputFilterPolicyInterface.h" +#include "InputListener.h" +#include "NotifyArgs.h" + +/** + * The C++ component of InputFilter designed as a wrapper around the rust callback implementation. + */ +namespace android { + +using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter; +using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent; +using aidl::com::android::server::inputflinger::IInputThread; +using IInputThreadCallback = + aidl::com::android::server::inputflinger::IInputThread::IInputThreadCallback; + +class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks { +public: + explicit InputFilterCallbacks(InputListenerInterface& listener, + InputFilterPolicyInterface& policy); + ~InputFilterCallbacks() override = default; + + uint32_t getModifierState(); + uint32_t getLockedModifierState(); + +private: + InputListenerInterface& mNextListener; + InputFilterPolicyInterface& mPolicy; + mutable std::mutex mLock; + struct StickyModifierState { + uint32_t modifierState; + uint32_t lockedModifierState; + } mStickyModifierState GUARDED_BY(mLock); + + ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override; + ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState, + int32_t lockedModifierState) override; + ndk::ScopedAStatus createInputFilterThread( + const std::shared_ptr<IInputThreadCallback>& callback, + std::shared_ptr<IInputThread>* aidl_return) override; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 296f2449ea..ae066c0f4a 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -127,7 +127,8 @@ std::shared_ptr<IInputFlingerRust> createInputFlingerRust() { */ InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, InputDispatcherPolicyInterface& dispatcherPolicy, - PointerChoreographerPolicyInterface& choreographerPolicy) { + PointerChoreographerPolicyInterface& choreographerPolicy, + InputFilterPolicyInterface& inputFilterPolicy) { mInputFlingerRust = createInputFlingerRust(); mDispatcher = createInputDispatcher(dispatcherPolicy); @@ -135,7 +136,8 @@ InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher)); if (ENABLE_INPUT_FILTER_RUST) { - mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust); + mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust, + inputFilterPolicy); mTracingStages.emplace_back( std::make_unique<TracedInputListener>("InputFilter", *mInputFilter)); } @@ -258,13 +260,16 @@ void InputManager::dump(std::string& dump) { } // Used by tests only. -binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { +binder::Status InputManager::createInputChannel(const std::string& name, + android::os::InputChannelCore* outChannel) { IPCThreadState* ipc = IPCThreadState::self(); - const int uid = ipc->getCallingUid(); + const uid_t uid = ipc->getCallingUid(); if (uid != AID_SHELL && uid != AID_ROOT) { - ALOGE("Invalid attempt to register input channel over IPC" - "from non shell/root entity (PID: %d)", ipc->getCallingPid()); - return binder::Status::ok(); + LOG(ERROR) << __func__ << " can only be called by SHELL or ROOT users, " + << "but was called from UID " << uid; + return binder::Status:: + fromExceptionCode(EX_SECURITY, + "This uid is not allowed to call createInputChannel"); } base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name); @@ -272,7 +277,7 @@ binder::Status InputManager::createInputChannel(const std::string& name, InputCh return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()), channel.error().message().c_str()); } - (*channel)->copyTo(*outChannel); + InputChannel::moveChannel(std::move(*channel), *outChannel); return binder::Status::ok(); } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index fa7db379e0..c479aaf9b6 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -29,6 +29,7 @@ #include <InputDispatcherInterface.h> #include <InputDispatcherPolicyInterface.h> +#include <InputFilterPolicyInterface.h> #include <PointerChoreographerPolicyInterface.h> #include <input/Input.h> #include <input/InputTransport.h> @@ -119,7 +120,8 @@ protected: public: InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, InputDispatcherPolicyInterface& dispatcherPolicy, - PointerChoreographerPolicyInterface& choreographerPolicy); + PointerChoreographerPolicyInterface& choreographerPolicy, + InputFilterPolicyInterface& inputFilterPolicy); status_t start() override; status_t stop() override; @@ -134,7 +136,8 @@ public: void dump(std::string& dump) override; status_t dump(int fd, const Vector<String16>& args) override; - binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; + binder::Status createInputChannel(const std::string& name, + android::os::InputChannelCore* outChannel) override; binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override; binder::Status setFocusedWindow(const gui::FocusRequest&) override; diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 0be4c327b7..3ac4285304 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -109,7 +109,9 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio 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); - pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + if (canUnfadeOnDisplay(displayId)) { + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + } const auto [x, y] = pc.getPosition(); NotifyMotionArgs newArgs(args); @@ -131,7 +133,9 @@ NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMo 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); - pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + if (canUnfadeOnDisplay(displayId)) { + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + } const auto [x, y] = pc.getPosition(); newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -140,7 +144,9 @@ NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMo newArgs.yCursorPosition = y; } else { // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer. - pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + if (canUnfadeOnDisplay(displayId)) { + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + } const auto [x, y] = pc.getPosition(); for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) { @@ -222,7 +228,8 @@ void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& pc.setPosition(x, y); if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) { pc.fade(PointerControllerInterface::Transition::IMMEDIATE); - } else { + pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED); + } else if (canUnfadeOnDisplay(args.displayId)) { pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); } } @@ -322,6 +329,10 @@ InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) return it != mInputDeviceInfos.end() ? &(*it) : nullptr; } +bool PointerChoreographer::canUnfadeOnDisplay(int32_t displayId) { + return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end(); +} + void PointerChoreographer::updatePointerControllersLocked() { std::set<int32_t /*displayId*/> mouseDisplaysToKeep; std::set<DeviceId> touchDevicesToKeep; @@ -341,7 +352,7 @@ void PointerChoreographer::updatePointerControllersLocked() { mMousePointersByDisplay.try_emplace(displayId, getMouseControllerConstructor(displayId)); auto [_, isNewMouseDevice] = mMouseDevices.emplace(info.getId()); - if (isNewMouseDevice || isNewMousePointer) { + if ((isNewMouseDevice || isNewMousePointer) && canUnfadeOnDisplay(displayId)) { mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE); } } @@ -512,6 +523,28 @@ bool PointerChoreographer::setPointerIcon( return true; } +void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) { + std::scoped_lock lock(mLock); + if (visible) { + mDisplaysWithPointersHidden.erase(displayId); + // We do not unfade the icons here, because we don't know when the last event happened. + return; + } + + mDisplaysWithPointersHidden.emplace(displayId); + + // Hide any icons that are currently visible on the display. + if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) { + const auto& [_, controller] = *it; + controller->fade(PointerControllerInterface::Transition::IMMEDIATE); + } + for (const auto& [_, controller] : mStylusPointersByDevice) { + if (controller->getDisplayId() == displayId) { + controller->fade(PointerControllerInterface::Transition::IMMEDIATE); + } + } +} + PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor( int32_t displayId) { std::function<std::shared_ptr<PointerControllerInterface>()> ctor = diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index f46419ec2e..6aab3aade0 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -67,6 +67,11 @@ public: */ virtual bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId, DeviceId deviceId) = 0; + /** + * Set whether pointer icons for mice, touchpads, and styluses should be visible on the + * given display. + */ + virtual void setPointerIconVisibility(int32_t displayId, bool visible) = 0; /** * This method may be called on any thread (usually by the input manager on a binder thread). @@ -89,6 +94,7 @@ public: void setStylusPointerIconEnabled(bool enabled) override; bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId, DeviceId deviceId) override; + void setPointerIconVisibility(int32_t displayId, bool visible) override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; @@ -110,6 +116,7 @@ private: std::pair<int32_t, PointerControllerInterface&> getDisplayIdAndMouseControllerLocked( int32_t associatedDisplayId) REQUIRES(mLock); InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock); + bool canUnfadeOnDisplay(int32_t displayId) REQUIRES(mLock); NotifyMotionArgs processMotion(const NotifyMotionArgs& args); NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); @@ -143,6 +150,7 @@ private: std::vector<DisplayViewport> mViewports GUARDED_BY(mLock); bool mShowTouchesEnabled GUARDED_BY(mLock); bool mStylusPointerIconEnabled GUARDED_BY(mLock); + std::set<int32_t /*displayId*/> mDisplaysWithPointersHidden; }; } // namespace android diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index 513cbfa7cc..293ad66966 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -285,5 +285,10 @@ { "name": "CtsInputHostTestCases" } + ], + "staged-platinum-postsubmit": [ + { + "name": "inputflinger_tests" + } ] } diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl index 14b41cd00c..994d1c4b1a 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl @@ -17,6 +17,8 @@ package com.android.server.inputflinger; import com.android.server.inputflinger.DeviceInfo; +import com.android.server.inputflinger.IInputThread; +import com.android.server.inputflinger.IInputThread.IInputThreadCallback; import com.android.server.inputflinger.InputFilterConfiguration; import com.android.server.inputflinger.KeyEvent; @@ -33,6 +35,12 @@ interface IInputFilter { interface IInputFilterCallbacks { /** Sends back a filtered key event */ void sendKeyEvent(in KeyEvent event); + + /** Sends back modifier state */ + void onModifierStateChanged(int modifierState, int lockedModifierState); + + /** Creates an Input filter thread */ + IInputThread createInputFilterThread(in IInputThreadCallback callback); } /** Returns if InputFilter is enabled */ diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl new file mode 100644 index 0000000000..2f6b8fc6ff --- /dev/null +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl @@ -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 com.android.server.inputflinger; + +/** Interface to handle and run things on an InputThread + * Exposes main functionality of InputThread.h to rust which internally used system/core/libutils + * infrastructure. + * + * <p> + * NOTE: Tried using rust provided threading infrastructure but that uses std::thread which doesn't + * have JNI support and can't call into Java policy that we use currently. libutils provided + * Thread.h also recommends against using std::thread and using the provided infrastructure that + * already provides way of attaching JniEnv to the created thread. So, we are using this interface + * to expose the InputThread infrastructure to rust. + * </p> + * TODO(b/321769871): Implement the threading infrastructure with JniEnv support in rust + */ +interface IInputThread { + /** Finish input thread (if not running, this call does nothing) */ + void finish(); + + /** Callbacks from C++ to call into inputflinger rust components */ + interface IInputThreadCallback { + /** + * The created thread will keep looping and calling this function. + * It's the responsibility of RUST component to appropriately put the thread to sleep and + * wake according to the use case. + */ + void loopOnce(); + } +}
\ No newline at end of file diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl index 3b2e88ba24..9984a6a9ea 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl @@ -22,4 +22,8 @@ package com.android.server.inputflinger; parcelable InputFilterConfiguration { // Threshold value for Bounce keys filter (check bounce_keys_filter.rs) long bounceKeysThresholdNs; + // If sticky keys filter is enabled (check sticky_keys_filter.rs) + boolean stickyKeysEnabled; + // Threshold value for Slow keys filter (check slow_keys_filter.rs) + long slowKeysThresholdNs; }
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index c7baceea70..6d71acc775 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // 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" @@ -49,6 +50,7 @@ filegroup { "Monitor.cpp", "TouchedWindow.cpp", "TouchState.cpp", + "trace/*.cpp", ], } @@ -72,6 +74,7 @@ cc_defaults { static_libs: [ "libattestation", "libgui_window_info_static", + "libperfetto_client_experimental", ], target: { android: { diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index f304712e8c..9dee66f0f8 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -20,22 +20,15 @@ namespace android::inputdispatcher { -Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, +Connection::Connection(std::unique_ptr<InputChannel> inputChannel, bool monitor, const IdGenerator& idGenerator) : status(Status::NORMAL), - inputChannel(inputChannel), monitor(monitor), - inputPublisher(inputChannel), + inputPublisher(std::move(inputChannel)), inputState(idGenerator) {} -const std::string Connection::getWindowName() const { - if (inputChannel != nullptr) { - return inputChannel->getName(); - } - if (monitor) { - return "monitor"; - } - return "?"; -} +sp<IBinder> Connection::getToken() const { + return inputPublisher.getChannel().getConnectionToken(); +}; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index c17baea0e5..a834a8cf86 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -42,7 +42,6 @@ public: }; Status status; - std::shared_ptr<InputChannel> inputChannel; // never null bool monitor; InputPublisher inputPublisher; InputState inputState; @@ -59,12 +58,14 @@ public: // yet received a "finished" response from the application. std::deque<std::unique_ptr<DispatchEntry>> waitQueue; - Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, + Connection(std::unique_ptr<InputChannel> inputChannel, bool monitor, const IdGenerator& idGenerator); - inline const std::string getInputChannelName() const { return inputChannel->getName(); } + inline const std::string getInputChannelName() const { + return inputPublisher.getChannel().getName(); + } - const std::string getWindowName() const; + sp<IBinder> getToken() const; }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h index c889b9b136..fe33d94504 100644 --- a/services/inputflinger/dispatcher/DebugConfig.h +++ b/services/inputflinger/dispatcher/DebugConfig.h @@ -98,13 +98,6 @@ const bool DEBUG_TOUCH_MODE = constexpr bool DEBUG_TOUCH_OCCLUSION = true; /** - * Log debug messages about the app switch latency optimization. - * Enable this via "adb shell setprop log.tag.InputDispatcherAppSwitch DEBUG" (requires restart) - */ -const bool DEBUG_APP_SWITCH = - android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "AppSwitch"); - -/** * Log debug messages about hover events. * Enable this via "adb shell setprop log.tag.InputDispatcherHover DEBUG" (requires restart) */ diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index cc0d49c15e..264dc03e70 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -164,6 +164,11 @@ std::string KeyEntry::getDescription() const { keyCode, scanCode, metaState, repeatCount, policyFlags); } +std::ostream& operator<<(std::ostream& out, const KeyEntry& keyEntry) { + out << keyEntry.getDescription(); + return out; +} + // --- TouchModeEntry --- TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId) @@ -277,9 +282,10 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> targetFlags, + ftl::Flags<InputTargetFlags> targetFlags, const ui::Transform& transform, const ui::Transform& rawTransform, - float globalScaleFactor) + float globalScaleFactor, gui::Uid targetUid, int64_t vsyncId, + std::optional<int32_t> windowId) : seq(nextSeq()), eventEntry(std::move(eventEntry)), targetFlags(targetFlags), @@ -287,7 +293,10 @@ DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, rawTransform(rawTransform), globalScaleFactor(globalScaleFactor), deliveryTime(0), - resolvedFlags(0) { + resolvedFlags(0), + targetUid(targetUid), + vsyncId(vsyncId), + windowId(windowId) { switch (this->eventEntry->type) { case EventEntry::Type::KEY: { const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*this->eventEntry); diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index e2e13c3fde..1298b5d511 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -17,7 +17,8 @@ #pragma once #include "InjectionState.h" -#include "InputTarget.h" +#include "InputTargetFlags.h" +#include "trace/EventTrackerInterface.h" #include <gui/InputApplication.h> #include <input/Input.h> @@ -125,6 +126,7 @@ struct KeyEntry : EventEntry { int32_t scanCode; int32_t metaState; nsecs_t downTime; + std::unique_ptr<trace::EventTrackerInterface> traceTracker; bool syntheticRepeat; // set to true for synthetic key repeats @@ -147,6 +149,8 @@ struct KeyEntry : EventEntry { std::string getDescription() const override; }; +std::ostream& operator<<(std::ostream& out, const KeyEntry& motionEntry); + struct MotionEntry : EventEntry { int32_t deviceId; uint32_t source; @@ -165,6 +169,7 @@ struct MotionEntry : EventEntry { nsecs_t downTime; std::vector<PointerProperties> pointerProperties; std::vector<PointerCoords> pointerCoords; + std::unique_ptr<trace::EventTrackerInterface> traceTracker; size_t getPointerCount() const { return pointerProperties.size(); } @@ -210,7 +215,7 @@ struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 std::shared_ptr<const EventEntry> eventEntry; // the event to dispatch - const ftl::Flags<InputTarget::Flags> targetFlags; + const ftl::Flags<InputTargetFlags> targetFlags; ui::Transform transform; ui::Transform rawTransform; float globalScaleFactor; @@ -222,17 +227,27 @@ struct DispatchEntry { int32_t resolvedFlags; + // Information about the dispatch window used for tracing. We avoid holding a window handle + // here because information in a window handle may be dynamically updated within the lifespan + // of this dispatch entry. + gui::Uid targetUid; + int64_t vsyncId; + // The window that this event is targeting. The only case when this windowId is not populated + // is when dispatching an event to a global monitor. + std::optional<int32_t> windowId; + DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform, - const ui::Transform& rawTransform, float globalScaleFactor); + ftl::Flags<InputTargetFlags> targetFlags, const ui::Transform& transform, + const ui::Transform& rawTransform, float globalScaleFactor, gui::Uid targetUid, + int64_t vsyncId, std::optional<int32_t> windowId); DispatchEntry(const DispatchEntry&) = delete; DispatchEntry& operator=(const DispatchEntry&) = delete; inline bool hasForegroundTarget() const { - return targetFlags.test(InputTarget::Flags::FOREGROUND); + return targetFlags.test(InputTargetFlags::FOREGROUND); } - inline bool isSplit() const { return targetFlags.test(InputTarget::Flags::SPLIT); } + inline bool isSplit() const { return targetFlags.test(InputTargetFlags::SPLIT); } private: static volatile int32_t sNextSeqAtomic; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 67e48a266d..c91ba38101 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -35,7 +35,7 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> #include <openssl/mem.h> -#include <powermanager/PowerManager.h> +#include <private/android_filesystem_config.h> #include <unistd.h> #include <utils/Trace.h> @@ -52,6 +52,9 @@ #include "Connection.h" #include "DebugConfig.h" #include "InputDispatcher.h" +#include "trace/InputTracer.h" +#include "trace/InputTracingPerfettoBackend.h" +#include "trace/ThreadedBackend.h" #define INDENT " " #define INDENT2 " " @@ -72,11 +75,34 @@ using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; namespace input_flags = com::android::input::flags; -static const bool REMOVE_APP_SWITCH_DROPS = input_flags::remove_app_switch_drops(); - namespace android::inputdispatcher { namespace { + +// Input tracing is only available on debuggable builds (userdebug and eng) when the feature +// flag is enabled. When the flag is changed, tracing will only be available after reboot. +bool isInputTracingEnabled() { + static const std::string buildType = base::GetProperty("ro.build.type", "user"); + static const bool isUserdebugOrEng = buildType == "userdebug" || buildType == "eng"; + return input_flags::enable_input_event_tracing() && isUserdebugOrEng; +} + +// Create the input tracing backend that writes to perfetto from a single thread. +std::unique_ptr<trace::InputTracingBackendInterface> createInputTracingBackendIfEnabled() { + if (!isInputTracingEnabled()) { + return nullptr; + } + return std::make_unique<trace::impl::ThreadedBackend<trace::impl::PerfettoBackend>>( + trace::impl::PerfettoBackend()); +} + +template <class Entry> +void ensureEventTraced(const Entry& entry) { + if (!entry.traceTracker) { + LOG(FATAL) << "Expected event entry to be traced, but it wasn't: " << entry; + } +} + // Temporarily releases a held mutex for the lifetime of the instance. // Named to match std::scoped_lock class scoped_unlock { @@ -94,10 +120,8 @@ const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::mil android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * HwTimeoutMultiplier()); -// Amount of time to allow for all pending events to be processed when an app switch -// key is on the way. This is used to preempt input dispatch and drop input events -// when an application takes too long to respond and the user has pressed an app switch key. -constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec +// The default minimum time gap between two user activity poke events. +const std::chrono::milliseconds DEFAULT_USER_ACTIVITY_POKE_INTERVAL = 100ms; const std::chrono::duration STALE_EVENT_TIMEOUT = std::chrono::seconds(10) * HwTimeoutMultiplier(); @@ -107,11 +131,6 @@ constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2 // Log a warning when an interception call takes longer than this to process. constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; -// Additional key latency in case a connection is still processing some motion events. -// This will help with the case when a user touched a button that opens a new window, -// and gives us the chance to dispatch the key to this new window. -constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms; - // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; @@ -252,6 +271,14 @@ Result<void> validateInputEvent(const InputEvent& event) { } } +std::bitset<MAX_POINTER_ID + 1> getPointerIds(const std::vector<PointerProperties>& pointers) { + std::bitset<MAX_POINTER_ID + 1> pointerIds; + for (const PointerProperties& pointer : pointers) { + pointerIds.set(pointer.id); + } + return pointerIds; +} + std::string dumpRegion(const Region& region) { if (region.isEmpty()) { return "<empty>"; @@ -348,46 +375,63 @@ size_t firstMarkedBit(T set) { return i; } -std::unique_ptr<DispatchEntry> createDispatchEntry( - const InputTarget& inputTarget, std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> inputTargetFlags) { - if (inputTarget.useDefaultPointerTransform()) { +std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerator, + const InputTarget& inputTarget, + std::shared_ptr<const EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> inputTargetFlags, + int64_t vsyncId) { + const bool zeroCoords = inputTargetFlags.test(InputTarget::Flags::ZERO_COORDS); + const sp<WindowInfoHandle> win = inputTarget.windowHandle; + const std::optional<int32_t> windowId = + win ? std::make_optional(win->getInfo()->id) : std::nullopt; + // Assume the only targets that are not associated with a window are global monitors, and use + // the system UID for global monitors for tracing purposes. + const gui::Uid uid = win ? win->getInfo()->ownerUid : gui::Uid(AID_SYSTEM); + + if (inputTarget.useDefaultPointerTransform() && !zeroCoords) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, inputTarget.displayTransform, - inputTarget.globalScaleFactor); + inputTarget.globalScaleFactor, uid, vsyncId, + windowId); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); - std::vector<PointerCoords> pointerCoords; - pointerCoords.resize(motionEntry.getPointerCount()); + std::vector<PointerCoords> pointerCoords{motionEntry.getPointerCount()}; - // Use the first pointer information to normalize all other pointers. This could be any pointer - // as long as all other pointers are normalized to the same value and the final DispatchEntry - // uses the transform for the normalized pointer. - const ui::Transform& firstPointerTransform = - inputTarget.pointerTransforms[firstMarkedBit(inputTarget.pointerIds)]; - ui::Transform inverseFirstTransform = firstPointerTransform.inverse(); - - // Iterate through all pointers in the event to normalize against the first. - for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount(); pointerIndex++) { - const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; - uint32_t pointerId = uint32_t(pointerProperties.id); - const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId]; - - pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); - // First, apply the current pointer's transform to update the coordinates into - // window space. - pointerCoords[pointerIndex].transform(currTransform); - // Next, apply the inverse transform of the normalized coordinates so the - // current coordinates are transformed into the normalized coordinate space. - pointerCoords[pointerIndex].transform(inverseFirstTransform); + const ui::Transform* transform = &kIdentityTransform; + const ui::Transform* displayTransform = &kIdentityTransform; + if (zeroCoords) { + std::for_each(pointerCoords.begin(), pointerCoords.end(), [](auto& pc) { pc.clear(); }); + } else { + // Use the first pointer information to normalize all other pointers. This could be any + // pointer as long as all other pointers are normalized to the same value and the final + // DispatchEntry uses the transform for the normalized pointer. + transform = + &inputTarget.getTransformForPointer(firstMarkedBit(inputTarget.getPointerIds())); + const ui::Transform inverseTransform = transform->inverse(); + displayTransform = &inputTarget.displayTransform; + + // Iterate through all pointers in the event to normalize against the first. + for (size_t i = 0; i < motionEntry.getPointerCount(); i++) { + PointerCoords& newCoords = pointerCoords[i]; + const auto pointerId = motionEntry.pointerProperties[i].id; + const ui::Transform& currTransform = inputTarget.getTransformForPointer(pointerId); + + newCoords.copyFrom(motionEntry.pointerCoords[i]); + // First, apply the current pointer's transform to update the coordinates into + // window space. + newCoords.transform(currTransform); + // Next, apply the inverse transform of the normalized coordinates so the + // current coordinates are transformed into the normalized coordinate space. + newCoords.transform(inverseTransform); + } } std::unique_ptr<MotionEntry> combinedMotionEntry = - std::make_unique<MotionEntry>(motionEntry.id, motionEntry.injectionState, + std::make_unique<MotionEntry>(idGenerator.nextId(), motionEntry.injectionState, motionEntry.eventTime, motionEntry.deviceId, motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, motionEntry.action, @@ -401,20 +445,11 @@ std::unique_ptr<DispatchEntry> createDispatchEntry( std::unique_ptr<DispatchEntry> dispatchEntry = std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, - firstPointerTransform, inputTarget.displayTransform, - inputTarget.globalScaleFactor); + *transform, *displayTransform, + inputTarget.globalScaleFactor, uid, vsyncId, windowId); return dispatchEntry; } -status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel, - std::unique_ptr<InputChannel>& clientChannel) { - std::unique_ptr<InputChannel> uniqueServerChannel; - status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel); - - serverChannel = std::move(uniqueServerChannel); - return result; -} - template <typename T> bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) { if (lhs == nullptr && rhs == nullptr) { @@ -630,15 +665,15 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, } // We should consider all hovering pointers here. But for now, just use the first one - const int32_t pointerId = entry.pointerProperties[0].id; + const PointerProperties& pointer = entry.pointerProperties[0]; std::set<sp<WindowInfoHandle>> oldWindows; if (oldState != nullptr) { - oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointerId); + oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointer.id); } std::set<sp<WindowInfoHandle>> newWindows = - newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointerId); + newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointer.id); // If the pointer is no longer in the new window set, send HOVER_EXIT. for (const sp<WindowInfoHandle>& oldWindow : oldWindows) { @@ -672,7 +707,7 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, } touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS; } - touchedWindow.addHoveringPointer(entry.deviceId, pointerId); + touchedWindow.addHoveringPointer(entry.deviceId, pointer); if (canReceiveForegroundTouches(*newWindow->getInfo())) { touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND; } @@ -698,7 +733,7 @@ void filterUntrustedTargets(TouchState& touchState, std::vector<InputTarget>& ta // TODO(b/282025641): simplify this code once InputTargets are being identified // separately from TouchedWindows. std::erase_if(targets, [&](const InputTarget& target) { - return target.inputChannel->getConnectionToken() == window.windowHandle->getToken(); + return target.connection->getToken() == window.windowHandle->getToken(); }); return true; } @@ -740,17 +775,82 @@ bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) { return true; } +/** + * Return true if stylus is currently down anywhere on the specified display, and false otherwise. + */ +bool isStylusActiveInDisplay( + int32_t displayId, + const std::unordered_map<int32_t /*displayId*/, TouchState>& touchStatesByDisplay) { + const auto it = touchStatesByDisplay.find(displayId); + if (it == touchStatesByDisplay.end()) { + return false; + } + const TouchState& state = it->second; + return state.hasActiveStylus(); +} + +Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) { + struct HashFunction { + size_t operator()(const WindowInfo& info) const { return info.id; } + }; + + std::unordered_set<WindowInfo, HashFunction> windowSet; + for (const WindowInfo& info : update.windowInfos) { + const auto [_, inserted] = windowSet.insert(info); + if (!inserted) { + return Error() << "Duplicate entry for " << info; + } + } + return {}; +} + +int32_t getUserActivityEventType(const EventEntry& eventEntry) { + switch (eventEntry.type) { + case EventEntry::Type::KEY: { + return USER_ACTIVITY_EVENT_BUTTON; + } + case EventEntry::Type::MOTION: { + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); + if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { + return USER_ACTIVITY_EVENT_TOUCH; + } + return USER_ACTIVITY_EVENT_OTHER; + } + default: { + LOG_ALWAYS_FATAL("%s events are not user activity", + ftl::enum_string(eventEntry.type).c_str()); + } + } +} + +std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellationMode( + CancelationOptions::Mode mode) { + switch (mode) { + case CancelationOptions::Mode::CANCEL_ALL_EVENTS: + return {true, true}; + case CancelationOptions::Mode::CANCEL_POINTER_EVENTS: + return {true, false}; + case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: + return {false, true}; + case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS: + return {false, true}; + } +} + } // namespace // --- InputDispatcher --- InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy) + : InputDispatcher(policy, createInputTracingBackendIfEnabled()) {} + +InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, + std::unique_ptr<trace::InputTracingBackendInterface> traceBackend) : mPolicy(policy), mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), - mAppSwitchSawKeyDown(false), - mAppSwitchDueTime(LLONG_MAX), + mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL), mNextUnblockedEvent(nullptr), mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), @@ -769,6 +869,12 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy) SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener); #endif mKeyRepeatState.lastKeyEntry = nullptr; + + if (traceBackend) { + mTracer = std::make_unique<trace::impl::InputTracer>(std::move(traceBackend)); + } + + mLastUserActivityTimes.fill(0); } InputDispatcher::~InputDispatcher() { @@ -781,7 +887,7 @@ InputDispatcher::~InputDispatcher() { while (!mConnectionsByToken.empty()) { std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second; - removeInputChannelLocked(connection->inputChannel->getConnectionToken(), /*notify=*/false); + removeInputChannelLocked(connection->getToken(), /*notify=*/false); } } @@ -812,7 +918,7 @@ void InputDispatcher::dispatchOnce() { // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) { - dispatchOnceInnerLocked(&nextWakeupTime); + dispatchOnceInnerLocked(/*byref*/ nextWakeupTime); } // Run all pending commands if there are any. @@ -901,7 +1007,7 @@ nsecs_t InputDispatcher::processAnrsLocked() { } connection->responsive = false; // Stop waking up for this unresponsive connection - mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); + mAnrTracker.eraseToken(connection->getToken()); onAnrLocked(connection); return LLONG_MIN; } @@ -911,15 +1017,14 @@ std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( if (connection->monitor) { return mMonitorDispatchingTimeout; } - const sp<WindowInfoHandle> window = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + const sp<WindowInfoHandle> window = getWindowHandleLocked(connection->getToken()); if (window != nullptr) { return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } return DEFAULT_INPUT_DISPATCHING_TIMEOUT; } -void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { +void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) { nsecs_t currentTime = now(); // Reset the key repeat timer whenever normal dispatch is suspended while the @@ -937,38 +1042,16 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { return; } - // Optimize latency of app switches. - // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has - // been pressed. When it expires, we preempt dispatch and drop all other pending events. - bool isAppSwitchDue; - if (!REMOVE_APP_SWITCH_DROPS) { - isAppSwitchDue = mAppSwitchDueTime <= currentTime; - if (mAppSwitchDueTime < *nextWakeupTime) { - *nextWakeupTime = mAppSwitchDueTime; - } - } - // Ready to start a new event. // If we don't already have a pending event, go grab one. if (!mPendingEvent) { if (mInboundQueue.empty()) { - if (!REMOVE_APP_SWITCH_DROPS) { - if (isAppSwitchDue) { - // The inbound queue is empty so the app switch key we were waiting - // for will never arrive. Stop waiting for it. - resetPendingAppSwitchLocked(false); - isAppSwitchDue = false; - } - } - // Synthesize a key repeat if appropriate. if (mKeyRepeatState.lastKeyEntry) { if (currentTime >= mKeyRepeatState.nextRepeatTime) { mPendingEvent = synthesizeKeyRepeatLocked(currentTime); } else { - if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { - *nextWakeupTime = mKeyRepeatState.nextRepeatTime; - } + nextWakeupTime = std::min(nextWakeupTime, mKeyRepeatState.nextRepeatTime); } } @@ -1057,16 +1140,6 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::Type::KEY: { std::shared_ptr<const KeyEntry> keyEntry = std::static_pointer_cast<const KeyEntry>(mPendingEvent); - if (!REMOVE_APP_SWITCH_DROPS) { - if (isAppSwitchDue) { - if (isAppSwitchKeyEvent(*keyEntry)) { - resetPendingAppSwitchLocked(true); - isAppSwitchDue = false; - } else if (dropReason == DropReason::NOT_DROPPED) { - dropReason = DropReason::APP_SWITCH; - } - } - } if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) { dropReason = DropReason::STALE; } @@ -1074,17 +1147,16 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { dropReason = DropReason::BLOCKED; } done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime); + if (done && mTracer) { + ensureEventTraced(*keyEntry); + mTracer->eventProcessingComplete(*keyEntry->traceTracker); + } break; } case EventEntry::Type::MOTION: { std::shared_ptr<const MotionEntry> motionEntry = std::static_pointer_cast<const MotionEntry>(mPendingEvent); - if (!REMOVE_APP_SWITCH_DROPS) { - if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { - dropReason = DropReason::APP_SWITCH; - } - } if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) { // The event is stale. However, only drop stale events if there isn't an ongoing // gesture. That would allow us to complete the processing of the current stroke. @@ -1104,17 +1176,17 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime); + if (done && mTracer) { + ensureEventTraced(*motionEntry); + mTracer->eventProcessingComplete(*motionEntry->traceTracker); + } break; } case EventEntry::Type::SENSOR: { std::shared_ptr<const SensorEntry> sensorEntry = std::static_pointer_cast<const SensorEntry>(mPendingEvent); - if (!REMOVE_APP_SWITCH_DROPS) { - if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { - dropReason = DropReason::APP_SWITCH; - } - } + // Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use // 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead. nsecs_t bootTime = systemTime(SYSTEM_TIME_BOOTTIME); @@ -1134,7 +1206,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { mLastDropReason = dropReason; releasePendingEventLocked(); - *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately + nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately } } @@ -1146,7 +1218,7 @@ bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) * Return true if the events preceding this incoming motion event should be dropped * Return false otherwise (the default behaviour) */ -bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { +bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) const { const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER); @@ -1188,16 +1260,6 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt } } - // Prevent getting stuck: if we have a pending key event, and some motion events that have not - // yet been processed by some connections, the dispatcher will wait for these motion - // events to be processed before dispatching the key event. This is because these motion events - // may cause a new window to be launched, which the user might expect to receive focus. - // To prevent waiting forever for such events, just send the key to the currently focused window - if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { - ALOGD("Received a new pointer down event, stop waiting for events to process and " - "just send the pending key event to the focused window."); - mKeyIsWaitingForEventsTimeout = now(); - } return false; } @@ -1211,27 +1273,12 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE case EventEntry::Type::KEY: { LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0, "Unexpected untrusted event."); - // Optimize app switch latency. - // If the application takes too long to catch up then we drop all events preceding - // the app switch key. - const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry); - if (!REMOVE_APP_SWITCH_DROPS) { - if (isAppSwitchKeyEvent(keyEntry)) { - if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) { - mAppSwitchSawKeyDown = true; - } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) { - if (mAppSwitchSawKeyDown) { - if (DEBUG_APP_SWITCH) { - ALOGD("App switch is pending!"); - } - mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT; - mAppSwitchSawKeyDown = false; - needWake = true; - } - } - } + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry); + if (mTracer) { + ensureEventTraced(keyEntry); } + // If a new up event comes in, and the pending event with same key code has been asked // to try again later because of the policy. We have to reset the intercept key wake up // time for it may have been handled in the policy and could be dropped. @@ -1252,10 +1299,29 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE case EventEntry::Type::MOTION: { LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0, "Unexpected untrusted event."); - if (shouldPruneInboundQueueLocked(static_cast<const MotionEntry&>(entry))) { + const auto& motionEntry = static_cast<const MotionEntry&>(entry); + if (mTracer) { + ensureEventTraced(motionEntry); + } + if (shouldPruneInboundQueueLocked(motionEntry)) { mNextUnblockedEvent = mInboundQueue.back(); needWake = true; } + + const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && + isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER); + if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { + // Prevent waiting too long for unprocessed events: if we have a pending key event, + // and some other events have not yet been processed, the dispatcher will wait for + // these events to be processed before dispatching the key event. This is because + // the unprocessed events may cause the focus to change (for example, by launching a + // new window or tapping a different window). To prevent waiting too long, we force + // the key to be sent to the currently focused window when a new tap comes in. + ALOGD("Received a new pointer down event, stop waiting for events to process and " + "just send the pending key event to the currently focused window."); + mKeyIsWaitingForEventsTimeout = now(); + needWake = true; + } break; } case EventEntry::Type::FOCUS: { @@ -1367,13 +1433,10 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } reason = "inbound event was dropped because input dispatch is disabled"; break; - case DropReason::APP_SWITCH: - ALOGI("Dropped event because of pending overdue app switch."); - reason = "inbound event was dropped because of pending overdue app switch"; - break; case DropReason::BLOCKED: - ALOGI("Dropped event because the current application is not responding and the user " - "has started interacting with a different application."); + LOG(INFO) << "Dropping because the current application is not responding and the user " + "has started interacting with a different application: " + << entry.getDescription(); reason = "inbound event was dropped because the current application is not responding " "and the user has started interacting with a different application"; break; @@ -1433,33 +1496,6 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } } -static bool isAppSwitchKeyCode(int32_t keyCode) { - return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL || - keyCode == AKEYCODE_APP_SWITCH; -} - -bool InputDispatcher::isAppSwitchKeyEvent(const KeyEntry& keyEntry) { - return !(keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry.keyCode) && - (keyEntry.policyFlags & POLICY_FLAG_TRUSTED) && - (keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER); -} - -bool InputDispatcher::isAppSwitchPendingLocked() const { - return mAppSwitchDueTime != LLONG_MAX; -} - -void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { - mAppSwitchDueTime = LLONG_MAX; - - if (DEBUG_APP_SWITCH) { - if (handled) { - ALOGD("App switch has arrived."); - } else { - ALOGD("App switch was abandoned."); - } - } -} - bool InputDispatcher::haveCommandsLocked() const { return !mCommandQueue.empty(); } @@ -1532,6 +1568,10 @@ std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cur entry->repeatCount + 1, entry->downTime); newEntry->syntheticRepeat = true; + if (mTracer) { + newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry); + } + mKeyRepeatState.lastKeyEntry = newEntry; mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay; return newEntry; @@ -1604,18 +1644,16 @@ void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bo void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry) { - std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken); - if (channel == nullptr) { - return; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken); + if (connection == nullptr) { + return; // Connection has gone away } - InputTarget target; - target.inputChannel = channel; entry->dispatchInProgress = true; std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") + - channel->getName(); + connection->getInputChannelName(); std::string reason = std::string("reason=").append(entry->reason); android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS; - dispatchEventLocked(currentTime, entry, {target}); + dispatchEventLocked(currentTime, entry, {{connection}}); } void InputDispatcher::dispatchPointerCaptureChangedLocked( @@ -1672,8 +1710,8 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( } } - auto channel = getInputChannelLocked(token); - if (channel == nullptr) { + auto connection = getConnectionLocked(token); + if (connection == nullptr) { // Window has gone away, clean up Pointer Capture state. mWindowTokenWithPointerCapture = nullptr; if (mCurrentPointerCaptureRequest.enable) { @@ -1681,10 +1719,8 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( } return; } - InputTarget target; - target.inputChannel = channel; entry->dispatchInProgress = true; - dispatchEventLocked(currentTime, entry, {target}); + dispatchEventLocked(currentTime, entry, {{connection}}); dropReason = DropReason::NOT_DROPPED; } @@ -1713,19 +1749,17 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked if (token == nullptr) { continue; } - std::shared_ptr<InputChannel> channel = getInputChannelLocked(token); - if (channel == nullptr) { - continue; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(token); + if (connection == nullptr) { + continue; // Connection has gone away } - InputTarget target; - target.inputChannel = channel; - inputTargets.push_back(target); + inputTargets.emplace_back(connection); } return inputTargets; } bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { // Preprocessing. if (!entry->dispatchInProgress) { if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN && @@ -1776,9 +1810,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con // Handle case where the policy asked us to try again later last time. if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) { if (currentTime < entry->interceptKeyWakeupTime) { - if (entry->interceptKeyWakeupTime < *nextWakeupTime) { - *nextWakeupTime = entry->interceptKeyWakeupTime; - } + nextWakeupTime = std::min(nextWakeupTime, entry->interceptKeyWakeupTime); return false; // wait until next wakeup } entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; @@ -1840,6 +1872,13 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); + if (mTracer) { + ensureEventTraced(*entry); + for (const auto& target : inputTargets) { + mTracer->dispatchToTargetHint(*entry->traceTracker, target); + } + } + // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; @@ -1858,7 +1897,7 @@ void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<const SensorEntry>& entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, " "source=0x%x, sensorType=%s", @@ -1898,7 +1937,7 @@ bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<const MotionEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { ATRACE_CALL(); // Preprocessing. if (!entry->dispatchInProgress) { @@ -1958,6 +1997,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS); CancelationOptions options(mode, "input event injection failed"); + options.displayId = entry->displayId; synthesizeCancelationEventsForMonitorsLocked(options); return true; } @@ -1965,6 +2005,13 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); + if (mTracer) { + ensureEventTraced(*entry); + for (const auto& target : inputTargets) { + mTracer->dispatchToTargetHint(*entry->traceTracker, target); + } + } + // Dispatch the motion. dispatchEventLocked(currentTime, entry, inputTargets); return true; @@ -1983,14 +2030,12 @@ void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowH void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry) { - std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken); - if (channel == nullptr) { - return; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken); + if (connection == nullptr) { + return; // Connection has gone away } - InputTarget target; - target.inputChannel = channel; entry->dispatchInProgress = true; - dispatchEventLocked(currentTime, entry, {target}); + dispatchEventLocked(currentTime, entry, {{connection}}); } void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) { @@ -2041,17 +2086,8 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, pokeUserActivityLocked(*eventEntry); for (const InputTarget& inputTarget : inputTargets) { - std::shared_ptr<Connection> connection = - getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); - if (connection != nullptr) { - prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); - } else { - if (DEBUG_DROPPED_EVENTS_VERBOSE) { - LOG(INFO) << "Dropping event delivery to target with channel " - << inputTarget.inputChannel->getName() - << " because it is no longer registered with the input dispatcher."; - } - } + std::shared_ptr<Connection> connection = inputTarget.connection; + prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } } @@ -2062,12 +2098,24 @@ void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection> // sending new pointers to the connection when it blocked, but focused events will continue to // pile up. ALOGW("Canceling events for %s because it is unresponsive", - connection->inputChannel->getName().c_str()); - if (connection->status == Connection::Status::NORMAL) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, - "application not responding"); - synthesizeCancelationEventsForConnectionLocked(connection, options); + connection->getInputChannelName().c_str()); + if (connection->status != Connection::Status::NORMAL) { + return; } + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, + "application not responding"); + + sp<WindowInfoHandle> windowHandle; + if (!connection->monitor) { + windowHandle = getWindowHandleLocked(connection->getToken()); + if (windowHandle == nullptr) { + // The window that is receiving this ANR was removed, so there is no need to generate + // cancellations, because the cancellations would have already been generated when + // the window was removed. + return; + } + } + synthesizeCancelationEventsForConnectionLocked(connection, options, windowHandle); } void InputDispatcher::resetNoFocusedWindowTimeoutLocked() { @@ -2124,7 +2172,8 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, // Start the timer // Wait to send key because there are unprocessed events that may cause focus to change mKeyIsWaitingForEventsTimeout = currentTime + - std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT) + std::chrono::duration_cast<std::chrono::nanoseconds>( + mPolicy.getKeyWaitingForEventsTimeout()) .count(); return true; } @@ -2143,7 +2192,7 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, } sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( - nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, + nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime, InputEventInjectionResult& outInjectionResult) { outInjectionResult = InputEventInjectionResult::FAILED; // Default result @@ -2182,7 +2231,7 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( ALOGW("Waiting because no window has focus but %s may eventually add a " "window when it finishes starting up. Will wait for %" PRId64 "ms", mAwaitedFocusedApplication->getName().c_str(), millis(timeout)); - *nextWakeupTime = *mNoFocusedWindowTimeoutTime; + nextWakeupTime = std::min(nextWakeupTime, *mNoFocusedWindowTimeoutTime); outInjectionResult = InputEventInjectionResult::PENDING; return nullptr; } else if (currentTime > *mNoFocusedWindowTimeoutTime) { @@ -2227,7 +2276,7 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( // prior input events. if (entry.type == EventEntry::Type::KEY) { if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { - *nextWakeupTime = *mKeyIsWaitingForEventsTimeout; + nextWakeupTime = std::min(nextWakeupTime, *mKeyIsWaitingForEventsTimeout); outInjectionResult = InputEventInjectionResult::PENDING; return nullptr; } @@ -2245,17 +2294,11 @@ std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( const std::vector<Monitor>& monitors) const { std::vector<Monitor> responsiveMonitors; std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors), - [this](const Monitor& monitor) REQUIRES(mLock) { - std::shared_ptr<Connection> connection = - getConnectionLocked(monitor.inputChannel->getConnectionToken()); - if (connection == nullptr) { - ALOGE("Could not find connection for monitor %s", - monitor.inputChannel->getName().c_str()); - return false; - } + [](const Monitor& monitor) REQUIRES(mLock) { + std::shared_ptr<Connection> connection = monitor.connection; if (!connection->responsive) { ALOGW("Unresponsive monitor %s will not get the new gesture", - connection->inputChannel->getName().c_str()); + connection->getInputChannelName().c_str()); return false; } return true; @@ -2329,7 +2372,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ const auto [x, y] = resolveTouchedPosition(entry); const int32_t pointerIndex = MotionEvent::getActionIndex(action); - const int32_t pointerId = entry.pointerProperties[pointerIndex].id; + const PointerProperties& pointer = entry.pointerProperties[pointerIndex]; // Outside targets should be added upon first dispatched DOWN event. That means, this should // be a pointer that would generate ACTION_DOWN, *and* touch should not already be down. const bool isStylus = isPointerFromStylus(entry, pointerIndex); @@ -2337,7 +2380,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( findTouchedWindowAtLocked(displayId, x, y, isStylus); if (isDown) { - targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointerId); + targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id); } // Handle the case where we did not find a window. if (newTouchedWindowHandle == nullptr) { @@ -2395,7 +2438,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( if (isHoverAction) { // The "windowHandle" is the target of this hovering pointer. - tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId); + tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer); } // Set target flags. @@ -2418,12 +2461,10 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Update the temporary touch state. if (!isHoverAction) { - std::bitset<MAX_POINTER_ID + 1> pointerIds; - pointerIds.set(pointerId); const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN; tempTouchState.addOrUpdateWindow(windowHandle, InputTarget::DispatchMode::AS_IS, - targetFlags, entry.deviceId, pointerIds, + targetFlags, entry.deviceId, {pointer}, isDownOrPointerDown ? std::make_optional(entry.eventTime) : std::nullopt); @@ -2446,7 +2487,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } tempTouchState.addOrUpdateWindow(wallpaper, InputTarget::DispatchMode::AS_IS, - wallpaperFlags, entry.deviceId, pointerIds, + wallpaperFlags, entry.deviceId, {pointer}, entry.eventTime); } } @@ -2457,12 +2498,12 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // make it pilfering. This will prevent other non-spy windows from getting this pointer, // which is a specific behaviour that we want. for (TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) && + if (touchedWindow.hasTouchingPointer(entry.deviceId, pointer.id) && touchedWindow.hasPilferingPointers(entry.deviceId)) { // This window is already pilfering some pointers, and this new pointer is also // going to it. Therefore, take over this pointer and don't give it to anyone // else. - touchedWindow.addPilferingPointer(entry.deviceId, pointerId); + touchedWindow.addPilferingPointer(entry.deviceId, pointer.id); } } @@ -2517,9 +2558,9 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( return {}; } - // Drop touch events if requested by input feature + // Do not slide events to the window which can not receive motion event if (newTouchedWindowHandle != nullptr && - shouldDropInput(entry, newTouchedWindowHandle)) { + !canWindowReceiveMotionLocked(newTouchedWindowHandle, entry)) { newTouchedWindowHandle = nullptr; } @@ -2531,8 +2572,8 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Make a slippery exit from the old window. std::bitset<MAX_POINTER_ID + 1> pointerIds; - const int32_t pointerId = entry.pointerProperties[0].id; - pointerIds.set(pointerId); + const PointerProperties& pointer = entry.pointerProperties[0]; + pointerIds.set(pointer.id); const TouchedWindow& touchedWindow = tempTouchState.getTouchedWindow(oldTouchedWindowHandle); @@ -2562,13 +2603,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, InputTarget::DispatchMode::SLIPPERY_ENTER, - targetFlags, entry.deviceId, pointerIds, + targetFlags, entry.deviceId, {pointer}, entry.eventTime); // Check if the wallpaper window should deliver the corresponding event. slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, - tempTouchState, entry.deviceId, pointerId, targets); - tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointerId, + tempTouchState, entry.deviceId, pointer, targets); + tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id, oldTouchedWindowHandle); } } @@ -2577,14 +2618,12 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { // If no split, we suppose all touched windows should receive pointer down. const int32_t pointerIndex = MotionEvent::getActionIndex(action); - for (size_t i = 0; i < tempTouchState.windows.size(); i++) { - TouchedWindow& touchedWindow = tempTouchState.windows[i]; + std::vector<PointerProperties> touchingPointers{entry.pointerProperties[pointerIndex]}; + for (TouchedWindow& touchedWindow : tempTouchState.windows) { // Ignore drag window for it should just track one pointer. if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) { continue; } - std::bitset<MAX_POINTER_ID + 1> touchingPointers; - touchingPointers.set(entry.pointerProperties[pointerIndex].id); touchedWindow.addTouchingPointers(entry.deviceId, touchingPointers); } } @@ -2636,7 +2675,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( for (InputTarget& target : targets) { if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) { sp<WindowInfoHandle> targetWindow = - getWindowHandleLocked(target.inputChannel->getConnectionToken()); + getWindowHandleLocked(target.connection->getToken()); if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) { target.flags |= InputTarget::Flags::ZERO_COORDS; } @@ -2655,13 +2694,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Output targets from the touch state. for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - std::bitset<MAX_POINTER_ID + 1> touchingPointers = + std::vector<PointerProperties> touchingPointers = touchedWindow.getTouchingPointers(entry.deviceId); - if (touchingPointers.none()) { + if (touchingPointers.empty()) { continue; } addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode, - touchedWindow.targetFlags, touchingPointers, + touchedWindow.targetFlags, getPointerIds(touchingPointers), touchedWindow.getDownTimeInTarget(entry.deviceId), targets); } @@ -2834,13 +2873,12 @@ std::optional<InputTarget> InputDispatcher::createInputTargetLocked( const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, std::optional<nsecs_t> firstDownTimeInTarget) const { - std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { + std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken()); + if (connection == nullptr) { ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str()); return {}; } - InputTarget inputTarget; - inputTarget.inputChannel = inputChannel; + InputTarget inputTarget{connection}; inputTarget.windowHandle = windowHandle; inputTarget.dispatchMode = dispatchMode; inputTarget.flags = targetFlags; @@ -2864,8 +2902,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { - return inputTarget.inputChannel->getConnectionToken() == - windowHandle->getToken(); + return inputTarget.connection->getToken() == windowHandle->getToken(); }); const WindowInfo* windowInfo = windowHandle->getInfo(); @@ -2905,8 +2942,7 @@ void InputDispatcher::addPointerWindowTargetLocked( std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { - return inputTarget.inputChannel->getConnectionToken() == - windowHandle->getToken(); + return inputTarget.connection->getToken() == windowHandle->getToken(); }); // This is a hack, because the actual entry could potentially be an ACTION_DOWN event that @@ -2955,8 +2991,7 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& if (monitorsIt == mGlobalMonitorsByDisplay.end()) return; for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) { - InputTarget target; - target.inputChannel = monitor.inputChannel; + InputTarget target{monitor.connection}; // target.firstDownTimeInTarget is not set for global monitors. It is only required in split // touch and global monitoring works as intended even without setting firstDownTimeInTarget if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { @@ -3158,6 +3193,21 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { // Not poking user activity if the event type does not represent a user activity return; } + + const int32_t eventType = getUserActivityEventType(eventEntry); + if (input_flags::rate_limit_user_activity_poke_in_dispatcher()) { + // Note that we're directly getting the time diff between the current event and the previous + // event. This is assuming that the first user event always happens at a timestamp that is + // greater than `mMinTimeBetweenUserActivityPokes` (otherwise, the first user event will + // wrongly be dropped). In real life, `mMinTimeBetweenUserActivityPokes` is a much smaller + // value than the potential first user activity event time, so this is ok. + std::chrono::nanoseconds timeSinceLastEvent = + std::chrono::nanoseconds(eventEntry.eventTime - mLastUserActivityTimes[eventType]); + if (timeSinceLastEvent < mMinTimeBetweenUserActivityPokes) { + return; + } + } + int32_t displayId = getTargetDisplayId(eventEntry); sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); const WindowInfo* windowDisablingUserActivityInfo = nullptr; @@ -3168,7 +3218,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } - int32_t eventType = USER_ACTIVITY_EVENT_OTHER; switch (eventEntry.type) { case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); @@ -3182,9 +3231,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } return; } - if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { - eventType = USER_ACTIVITY_EVENT_TOUCH; - } break; } case EventEntry::Type::KEY: { @@ -3208,7 +3254,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { return; } - eventType = USER_ACTIVITY_EVENT_BUTTON; break; } default: { @@ -3218,6 +3263,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } + mLastUserActivityTimes[eventType] = eventEntry.eventTime; auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]() REQUIRES(mLock) { scoped_unlock unlock(mLock); @@ -3237,7 +3283,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, " "globalScaleFactor=%f, pointerIds=%s %s", connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(), - inputTarget.globalScaleFactor, bitsetToString(inputTarget.pointerIds).c_str(), + inputTarget.globalScaleFactor, bitsetToString(inputTarget.getPointerIds()).c_str(), inputTarget.getPointerInfoString().c_str()); } @@ -3259,7 +3305,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ftl::enum_string(eventEntry->type).c_str()); const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry); - if (inputTarget.pointerIds.count() != originalMotionEntry.getPointerCount()) { + if (inputTarget.getPointerIds().count() != originalMotionEntry.getPointerCount()) { if (!inputTarget.firstDownTimeInTarget.has_value()) { logDispatchStateLocked(); LOG(FATAL) << "Splitting motion events requires a down time to be set for the " @@ -3268,7 +3314,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, << originalMotionEntry.getDescription(); } std::unique_ptr<MotionEntry> splitMotionEntry = - splitMotionEvent(originalMotionEntry, inputTarget.pointerIds, + splitMotionEvent(originalMotionEntry, inputTarget.getPointerIds(), inputTarget.firstDownTimeInTarget.value()); if (!splitMotionEntry) { return; // split event was dropped @@ -3316,10 +3362,18 @@ void InputDispatcher::enqueueDispatchEntryAndStartDispatchCycleLocked( void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection, std::shared_ptr<const EventEntry> eventEntry, const InputTarget& inputTarget) { + const bool isKeyOrMotion = eventEntry->type == EventEntry::Type::KEY || + eventEntry->type == EventEntry::Type::MOTION; + if (isKeyOrMotion && !inputTarget.windowHandle && !connection->monitor) { + LOG(FATAL) << "All InputTargets for non-monitors must be associated with a window; target: " + << inputTarget << " connection: " << connection->getInputChannelName() + << " entry: " << eventEntry->getDescription(); + } // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr<DispatchEntry> dispatchEntry = - createDispatchEntry(inputTarget, eventEntry, inputTarget.flags); + createDispatchEntry(mIdGenerator, inputTarget, eventEntry, inputTarget.flags, + mWindowInfosVsyncId); // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a // different EventEntry than what was passed in. @@ -3377,30 +3431,46 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED)) { resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; } + if (dispatchEntry->targetFlags.test(InputTarget::Flags::NO_FOCUS_CHANGE)) { + resolvedFlags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE; + } dispatchEntry->resolvedFlags = resolvedFlags; if (resolvedAction != motionEntry.action) { + std::optional<std::vector<PointerProperties>> usingProperties; + std::optional<std::vector<PointerCoords>> usingCoords; + if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT || + resolvedAction == AMOTION_EVENT_ACTION_CANCEL) { + // This is a HOVER_EXIT or an ACTION_CANCEL event that was synthesized by + // the dispatcher, and therefore the coordinates of this event are currently + // incorrect. These events should use the coordinates of the last dispatched + // ACTION_MOVE or HOVER_MOVE. We need to query InputState to get this data. + const bool hovering = resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT; + std::optional<std::pair<std::vector<PointerProperties>, + std::vector<PointerCoords>>> + pointerInfo = + connection->inputState.getPointersOfLastEvent(motionEntry, + hovering); + if (pointerInfo) { + usingProperties = pointerInfo->first; + 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, - motionEntry.pointerProperties, - motionEntry.pointerCoords); + 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)); if (ATRACE_ENABLED()) { std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32 ") to MotionEvent(id=0x%" PRIx32 ").", @@ -3424,8 +3494,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio << connection->getInputChannelName() << " with event " << cancelEvent->getDescription(); std::unique_ptr<DispatchEntry> cancelDispatchEntry = - createDispatchEntry(inputTarget, std::move(cancelEvent), - ftl::Flags<InputTarget::Flags>()); + createDispatchEntry(mIdGenerator, inputTarget, std::move(cancelEvent), + ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId); // Send these cancel events to the queue before sending the event from the new // device. @@ -3438,15 +3508,14 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio << "~ dropping inconsistent event: " << *dispatchEntry; return; // skip the inconsistent event } - - if ((resolvedMotion->flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) && + if ((dispatchEntry->resolvedFlags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) && (resolvedMotion->policyFlags & POLICY_FLAG_TRUSTED)) { // Skip reporting pointer down outside focus to the policy. break; } dispatchPointerDownOutsideFocus(resolvedMotion->source, resolvedMotion->action, - inputTarget.inputChannel->getConnectionToken()); + inputTarget.connection->getToken()); break; } @@ -3529,13 +3598,9 @@ void InputDispatcher::processInteractionsLocked(const EventEntry& entry, continue; // Skip windows that receive ACTION_OUTSIDE } - sp<IBinder> token = target.inputChannel->getConnectionToken(); - std::shared_ptr<Connection> connection = getConnectionLocked(token); - if (connection == nullptr) { - continue; - } + sp<IBinder> token = target.connection->getToken(); newConnectionTokens.insert(std::move(token)); - newConnections.emplace_back(connection); + newConnections.emplace_back(target.connection); if (target.windowHandle) { interactionUids.emplace(target.windowHandle->getInfo()->ownerUid); } @@ -3555,7 +3620,7 @@ void InputDispatcher::processInteractionsLocked(const EventEntry& entry, std::string targetList; for (const std::shared_ptr<Connection>& connection : newConnections) { - targetList += connection->getWindowName() + ", "; + targetList += connection->getInputChannelName() + ", "; } std::string message = "Interaction with: " + targetList; if (targetList.empty()) { @@ -3593,6 +3658,7 @@ status_t InputDispatcher::publishMotionEvent(Connection& connection, PointerCoords scaledCoords[MAX_POINTERS]; const PointerCoords* usingCoords = motionEntry.pointerCoords.data(); + // TODO(b/316355518): Do not modify coords before dispatch. // Set the X and Y offset and X and Y scale depending on the input source. if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) && !(dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS))) { @@ -3607,12 +3673,6 @@ status_t InputDispatcher::publishMotionEvent(Connection& connection, } usingCoords = scaledCoords; } - } else if (dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS)) { - // We don't want the dispatch target to know the coordinates - for (uint32_t i = 0; i < motionEntry.getPointerCount(); i++) { - scaledCoords[i].clear(); - } - usingCoords = scaledCoords; } std::array<uint8_t, 32> hmac = getSignature(motionEntry, dispatchEntry); @@ -3668,6 +3728,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, keyEntry.keyCode, keyEntry.scanCode, keyEntry.metaState, keyEntry.repeatCount, keyEntry.downTime, keyEntry.eventTime); + if (mTracer) { + mTracer->traceEventDispatch(*dispatchEntry, keyEntry.traceTracker.get()); + } break; } @@ -3676,7 +3739,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, LOG(INFO) << "Publishing " << *dispatchEntry << " to " << connection->getInputChannelName(); } + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); status = publishMotionEvent(*connection, *dispatchEntry); + if (mTracer) { + mTracer->traceEventDispatch(*dispatchEntry, motionEntry.traceTracker.get()); + } break; } @@ -3761,7 +3828,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, connection->outboundQueue.erase(connection->outboundQueue.begin()); traceOutboundQueueLength(*connection); if (connection->responsive) { - mAnrTracker.insert(timeoutTime, connection->inputChannel->getConnectionToken()); + mAnrTracker.insert(timeoutTime, connection->getToken()); } traceWaitQueueLength(*connection); } @@ -3814,8 +3881,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str(), seq, toString(handled)); } - if (connection->status == Connection::Status::BROKEN || - connection->status == Connection::Status::ZOMBIE) { + if (connection->status != Connection::Status::NORMAL) { return; } @@ -3852,7 +3918,7 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, auto command = [this, connection]() REQUIRES(mLock) { scoped_unlock unlock(mLock); - mPolicy.notifyInputChannelBroken(connection->inputChannel->getConnectionToken()); + mPolicy.notifyInputChannelBroken(connection->getToken()); }; postCommandLocked(std::move(command)); } @@ -3910,10 +3976,9 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok if (shouldReportMetricsForConnection(*connection)) { const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result); - mLatencyTracker - .trackGraphicsLatency(timeline.inputEventId, - connection->inputChannel->getConnectionToken(), - std::move(timeline.graphicsTimeline)); + mLatencyTracker.trackGraphicsLatency(timeline.inputEventId, + connection->getToken(), + std::move(timeline.graphicsTimeline)); } } gotOne = true; @@ -3934,8 +3999,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok } else { // Monitor channels are never explicitly unregistered. // We do it automatically when the remote endpoint is closed so don't warn about them. - const bool stillHaveWindowHandle = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr; + const bool stillHaveWindowHandle = getWindowHandleLocked(connection->getToken()) != nullptr; notify = !connection->monitor && stillHaveWindowHandle; if (notify) { ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x", @@ -3944,39 +4008,85 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok } // Remove the channel. - removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify); + removeInputChannelLocked(connection->getToken(), notify); return 0; // remove the callback } void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options) { - for (const auto& [token, connection] : mConnectionsByToken) { - synthesizeCancelationEventsForConnectionLocked(connection, options); + // Cancel windows (i.e. non-monitors). + // A channel must have at least one window to receive any input. If a window was removed, the + // event streams directed to the window will already have been canceled during window removal. + // So there is no need to generate cancellations for connections without any windows. + const auto [cancelPointers, cancelNonPointers] = expandCancellationMode(options.mode); + // Generate cancellations for touched windows first. This is to avoid generating cancellations + // through a non-touched window if there are more than one window for an input channel. + if (cancelPointers) { + for (const auto& [displayId, touchState] : mTouchStatesByDisplay) { + if (options.displayId.has_value() && options.displayId != displayId) { + continue; + } + for (const auto& touchedWindow : touchState.windows) { + synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); + } + } } + // Follow up by generating cancellations for all windows, because we don't explicitly track + // the windows that have an ongoing focus event stream. + if (cancelNonPointers) { + for (const auto& [_, handles] : mWindowHandlesByDisplay) { + for (const auto& windowHandle : handles) { + synthesizeCancelationEventsForWindowLocked(windowHandle, options); + } + } + } + + // Cancel monitors. + synthesizeCancelationEventsForMonitorsLocked(options); } void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { - synthesizeCancelationEventsForInputChannelLocked(monitor.inputChannel, options); + synthesizeCancelationEventsForConnectionLocked(monitor.connection, options, + /*window=*/nullptr); } } } -void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( - const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) { - std::shared_ptr<Connection> connection = getConnectionLocked(channel->getConnectionToken()); - if (connection == nullptr) { - return; +void InputDispatcher::synthesizeCancelationEventsForWindowLocked( + const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options, + const std::shared_ptr<Connection>& connection) { + if (windowHandle == nullptr) { + LOG(FATAL) << __func__ << ": Window handle must not be null"; + } + if (connection) { + // The connection can be optionally provided to avoid multiple lookups. + if (windowHandle->getToken() != connection->getToken()) { + LOG(FATAL) << __func__ + << ": Wrong connection provided for window: " << windowHandle->getName(); + } } - synthesizeCancelationEventsForConnectionLocked(connection, options); + std::shared_ptr<Connection> resolvedConnection = + connection ? connection : getConnectionLocked(windowHandle->getToken()); + if (!resolvedConnection) { + LOG(DEBUG) << __func__ << "No connection found for window: " << windowHandle->getName(); + return; + } + synthesizeCancelationEventsForConnectionLocked(resolvedConnection, options, windowHandle); } void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( - const std::shared_ptr<Connection>& connection, const CancelationOptions& options) { - if (connection->status == Connection::Status::BROKEN) { + const std::shared_ptr<Connection>& connection, const CancelationOptions& options, + const sp<WindowInfoHandle>& window) { + if (!connection->monitor && window == nullptr) { + LOG(FATAL) << __func__ + << ": Cannot send event to non-monitor channel without a window - channel: " + << connection->getInputChannelName(); + } + if (connection->status != Connection::Status::NORMAL) { return; } @@ -4002,8 +4112,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const bool wasEmpty = connection->outboundQueue.empty(); // The target to use if we don't find a window associated with the channel. - const InputTarget fallbackTarget{.inputChannel = connection->inputChannel}; - const auto& token = connection->inputChannel->getConnectionToken(); + const InputTarget fallbackTarget{connection}; + const auto& token = connection->getToken(); for (size_t i = 0; i < cancelationEvents.size(); i++) { std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]); @@ -4012,10 +4122,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( switch (cancelationEventEntry->type) { case EventEntry::Type::KEY: { const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry); - const std::optional<int32_t> targetDisplay = keyEntry.displayId != ADISPLAY_ID_NONE - ? std::make_optional(keyEntry.displayId) - : std::nullopt; - if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { + if (window) { addWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS, /*targetFlags=*/{}, keyEntry.downTime, targets); } else { @@ -4026,11 +4133,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } case EventEntry::Type::MOTION: { const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry); - const std::optional<int32_t> targetDisplay = - motionEntry.displayId != ADISPLAY_ID_NONE - ? std::make_optional(motionEntry.displayId) - : std::nullopt; - if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { + if (window) { std::bitset<MAX_POINTER_ID + 1> pointerIds; for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount(); pointerIndex++) { @@ -4088,7 +4191,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( const nsecs_t downTime, const std::shared_ptr<Connection>& connection, ftl::Flags<InputTarget::Flags> targetFlags) { - if (connection->status == Connection::Status::BROKEN) { + if (connection->status != Connection::Status::NORMAL) { return; } @@ -4104,8 +4207,12 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( connection->getInputChannelName().c_str(), downEvents.size()); } - sp<WindowInfoHandle> windowHandle = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + const auto [_, touchedWindowState, displayId] = + findTouchStateWindowAndDisplayLocked(connection->getToken()); + if (touchedWindowState == nullptr) { + LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token"; + } + const auto& windowHandle = touchedWindowState->windowHandle; const bool wasEmpty = connection->outboundQueue.empty(); for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { @@ -4123,8 +4230,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( targetFlags, pointerIds, motionEntry.downTime, targets); } else { - targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel, - .flags = targetFlags}); + targets.emplace_back(connection, targetFlags); const auto it = mDisplayInfos.find(motionEntry.displayId); if (it != mDisplayInfos.end()) { targets.back().displayTransform = it->second.transform; @@ -4159,17 +4265,6 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } } -void InputDispatcher::synthesizeCancelationEventsForWindowLocked( - const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) { - if (windowHandle != nullptr) { - std::shared_ptr<Connection> wallpaperConnection = - getConnectionLocked(windowHandle->getToken()); - if (wallpaperConnection != nullptr) { - synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options); - } - } -} - std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds, nsecs_t splitDownTime) { @@ -4359,6 +4454,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { args.deviceId, args.source, args.displayId, policyFlags, args.action, flags, keyCode, args.scanCode, metaState, repeatCount, args.downTime); + if (mTracer) { + newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry); + } needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); @@ -4427,7 +4525,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { policyFlags |= POLICY_FLAG_TRUSTED; android::base::Timer t; - mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags); + mPolicy.interceptMotionBeforeQueueing(args.displayId, args.source, args.action, args.eventTime, + policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", std::to_string(t.duration().count()).c_str()); @@ -4484,6 +4583,9 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { args.yPrecision, args.xCursorPosition, args.yCursorPosition, args.downTime, args.pointerProperties, args.pointerCoords); + if (mTracer) { + newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry); + } if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER && @@ -4671,6 +4773,9 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), incomingKey.getDownTime()); + if (mTracer) { + injectedEntry->traceTracker = mTracer->traceInboundEvent(*injectedEntry); + } injectedEntries.push(std::move(injectedEntry)); break; } @@ -4688,7 +4793,9 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev if (!(policyFlags & POLICY_FLAG_FILTERED)) { nsecs_t eventTime = motionEvent.getEventTime(); android::base::Timer t; - mPolicy.interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags); + mPolicy.interceptMotionBeforeQueueing(displayId, motionEvent.getSource(), + motionEvent.getAction(), eventTime, + /*byref*/ policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", std::to_string(t.duration().count()).c_str()); @@ -4726,6 +4833,9 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev samplePointerCoords + pointerCount)); transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform()); + if (mTracer) { + injectedEntry->traceTracker = mTracer->traceInboundEvent(*injectedEntry); + } injectedEntries.push(std::move(injectedEntry)); for (size_t i = motionEvent.getHistorySize(); i > 0; i--) { sampleEventTimes += 1; @@ -4848,7 +4958,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu break; } default: { - ALOGE("Cannot verify events of type %" PRId32, event.getType()); + LOG(ERROR) << "Cannot verify events of type " << ftl::enum_string(event.getType()); return nullptr; } } @@ -5056,16 +5166,14 @@ bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& w return false; } - return true; -} - -std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked( - const sp<IBinder>& token) const { - auto connectionIt = mConnectionsByToken.find(token); - if (connectionIt == mConnectionsByToken.end()) { - return nullptr; + // Ignore touches if stylus is down anywhere on screen + if (info.inputConfig.test(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH) && + isStylusActiveInDisplay(info.displayId, mTouchStatesByDisplay)) { + LOG(INFO) << "Dropping touch from " << window->getName() << " because stylus is active"; + return false; } - return connectionIt->second->inputChannel; + + return true; } void InputDispatcher::updateWindowHandlesForDisplayLocked( @@ -5087,7 +5195,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( std::vector<sp<WindowInfoHandle>> newHandles; for (const sp<WindowInfoHandle>& handle : windowInfoHandles) { const WindowInfo* info = handle->getInfo(); - if (getInputChannelLocked(handle->getToken()) == nullptr) { + if (getConnectionLocked(handle->getToken()) == nullptr) { const bool noInputChannel = info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); const bool canReceiveInput = @@ -5166,6 +5274,7 @@ void InputDispatcher::setInputWindowsLocked( // Copy old handles for release if they are no longer present. const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); + const sp<WindowInfoHandle> removedFocusedWindowHandle = getFocusedWindowHandleLocked(displayId); updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId); @@ -5174,7 +5283,7 @@ void InputDispatcher::setInputWindowsLocked( std::optional<FocusResolver::FocusChanges> changes = mFocusResolver.setInputWindows(displayId, windowHandles); if (changes) { - onFocusChangedLocked(*changes); + onFocusChangedLocked(*changes, removedFocusedWindowHandle); } std::unordered_map<int32_t, TouchState>::iterator stateIt = @@ -5186,19 +5295,16 @@ void InputDispatcher::setInputWindowsLocked( if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) { LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName() << " in display %" << displayId; - std::shared_ptr<InputChannel> touchedInputChannel = - getInputChannelLocked(touchedWindow.windowHandle->getToken()); - if (touchedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, - "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); - // Since we are about to drop the touch, cancel the events for the wallpaper as - // well. - if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && - touchedWindow.windowHandle->getInfo()->inputConfig.test( - gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { - sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow(); - synthesizeCancelationEventsForWindowLocked(wallpaper, options); + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, + "touched window was removed"); + synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); + // Since we are about to drop the touch, cancel the events for the wallpaper as + // well. + if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && + touchedWindow.windowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { + if (const auto& ww = state.getWallpaperWindow(); ww) { + synthesizeCancelationEventsForWindowLocked(ww, options); } } state.windows.erase(state.windows.begin() + i); @@ -5268,6 +5374,14 @@ void InputDispatcher::setFocusedApplicationLocked( resetNoFocusedWindowTimeoutLocked(); } +void InputDispatcher::setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) { + if (interval.count() < 0) { + LOG_ALWAYS_FATAL("Minimum time between user activity pokes should be >= 0"); + } + std::scoped_lock _l(mLock); + mMinTimeBetweenUserActivityPokes = interval; +} + /** * Sets the focused display, which is responsible for receiving focus-dispatched input events where * the display not specified. @@ -5288,15 +5402,16 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { sp<IBinder> oldFocusedWindowToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId); if (oldFocusedWindowToken != nullptr) { - std::shared_ptr<InputChannel> inputChannel = - getInputChannelLocked(oldFocusedWindowToken); - if (inputChannel != nullptr) { - CancelationOptions - options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, - "The display which contains this window no longer has focus."); - options.displayId = ADISPLAY_ID_NONE; - synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); + const auto windowHandle = + getWindowHandleLocked(oldFocusedWindowToken, mFocusedDisplayId); + if (windowHandle == nullptr) { + LOG(FATAL) << __func__ << ": Previously focused token did not have a window"; } + CancelationOptions + options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, + "The display which contains this window no longer has focus."); + options.displayId = ADISPLAY_ID_NONE; + synthesizeCancelationEventsForWindowLocked(windowHandle, options); } mFocusedDisplayId = displayId; @@ -5449,8 +5564,8 @@ InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) return std::make_tuple(nullptr, nullptr, ADISPLAY_ID_DEFAULT); } -bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop) { +bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool isDragDrop) { if (fromToken == toToken) { if (DEBUG_FOCUS) { ALOGD("Trivial transfer to same window."); @@ -5476,22 +5591,22 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } const int32_t deviceId = *deviceIds.begin(); - sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); - if (toWindowHandle == nullptr) { - ALOGW("Cannot transfer touch because to window not found."); + const sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; + const sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); + if (!toWindowHandle) { + ALOGW("Cannot transfer touch because the transfer target window was not found."); return false; } if (DEBUG_FOCUS) { - ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", + ALOGD("%s: fromWindowHandle=%s, toWindowHandle=%s", __func__, touchedWindow->windowHandle->getName().c_str(), toWindowHandle->getName().c_str()); } // Erase old window. ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags; - std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->getTouchingPointers(deviceId); - sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; + std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId); state->removeWindowByToken(fromToken); // Add new window. @@ -5501,18 +5616,20 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) { newTargetFlags |= InputTarget::Flags::FOREGROUND; } + // Transferring touch focus using this API should not effect the focused window. + newTargetFlags |= InputTarget::Flags::NO_FOCUS_CHANGE; state->addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags, - deviceId, pointerIds, downTimeInTarget); + deviceId, pointers, downTimeInTarget); // Store the dragging window. if (isDragDrop) { - if (pointerIds.count() != 1) { + if (pointers.size() != 1) { ALOGW("The drag and drop cannot be started when there is no pointer or more than 1" " pointer on the window."); return false; } // Track the pointer id for drag window and generate the drag state. - const size_t id = firstMarkedBit(pointerIds); + const size_t id = pointers.begin()->id; mDragState = std::make_unique<DragState>(toWindowHandle, id); } @@ -5523,13 +5640,13 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "transferring touch from this window to another window"); - synthesizeCancelationEventsForConnectionLocked(fromConnection, options); + synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection); synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection, newTargetFlags); // Check if the wallpaper window should deliver the corresponding event. transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle, - *state, deviceId, pointerIds); + *state, deviceId, pointers); } } // release lock @@ -5568,7 +5685,8 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindowLocked(int32_t } // Binder call -bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken, int32_t displayId) { +bool InputDispatcher::transferTouchOnDisplay(const sp<IBinder>& destChannelToken, + int32_t displayId) { sp<IBinder> fromToken; { // acquire lock std::scoped_lock _l(mLock); @@ -5588,7 +5706,7 @@ bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken, int32_t fromToken = from->getToken(); } // release lock - return transferTouchFocus(fromToken, destChannelToken); + return transferTouchGesture(fromToken, destChannelToken); } void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { @@ -5758,11 +5876,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { if (!mConnectionsByToken.empty()) { dump += INDENT "Connections:\n"; for (const auto& [token, connection] : mConnectionsByToken) { - dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " + dump += StringPrintf(INDENT2 "%i: channelName='%s', " "status=%s, monitor=%s, responsive=%s\n", - connection->inputChannel->getFd().get(), + connection->inputPublisher.getChannel().getFd(), connection->getInputChannelName().c_str(), - connection->getWindowName().c_str(), ftl::enum_string(connection->status).c_str(), toString(connection->monitor), toString(connection->responsive)); @@ -5792,16 +5909,6 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += INDENT "Connections: <none>\n"; } - dump += "input_flags::remove_app_switch_drops() = "; - dump += toString(REMOVE_APP_SWITCH_DROPS); - dump += "\n"; - if (isAppSwitchPendingLocked()) { - dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n", - ns2ms(mAppSwitchDueTime - now())); - } else { - dump += INDENT "AppSwitch: not pending\n"; - } - if (!mTouchModePerDisplay.empty()) { dump += INDENT "TouchModePerDisplay:\n"; for (const auto& [displayId, touchMode] : mTouchModePerDisplay) { @@ -5818,14 +5925,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { ns2ms(mConfig.keyRepeatTimeout)); dump += mLatencyTracker.dump(INDENT2); dump += mLatencyAggregator.dump(INDENT2); + dump += INDENT "InputTracer: "; + dump += mTracer == nullptr ? "Disabled" : "Enabled"; } void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const { const size_t numMonitors = monitors.size(); for (size_t i = 0; i < numMonitors; i++) { const Monitor& monitor = monitors[i]; - const std::shared_ptr<InputChannel>& channel = monitor.inputChannel; - dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str()); + const std::shared_ptr<Connection>& connection = monitor.connection; + dump += StringPrintf(INDENT2 "%zu: '%s', ", i, connection->getInputChannelName().c_str()); dump += "\n"; } } @@ -5855,20 +5964,20 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const { // acquire lock std::scoped_lock _l(mLock); const sp<IBinder>& token = serverChannel->getConnectionToken(); - auto&& fd = serverChannel->getFd(); + const int fd = serverChannel->getFd(); std::shared_ptr<Connection> connection = std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false, mIdGenerator); - if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { + auto [_, inserted] = mConnectionsByToken.try_emplace(token, connection); + if (!inserted) { ALOGE("Created a new connection, but the token %p is already known", token.get()); } - mConnectionsByToken.emplace(token, connection); std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr); } // release lock @@ -5880,9 +5989,9 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId, const std::string& name, gui::Pid pid) { - std::shared_ptr<InputChannel> serverChannel; + std::unique_ptr<InputChannel> serverChannel; std::unique_ptr<InputChannel> clientChannel; - status_t result = openInputChannelPair(name, serverChannel, clientChannel); + status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); if (result) { return base::Error(result) << "Failed to open input channel pair with name " << name; } @@ -5895,21 +6004,23 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_ << " without a specified display."; } - std::shared_ptr<Connection> connection = - std::make_shared<Connection>(serverChannel, /*monitor=*/true, mIdGenerator); const sp<IBinder>& token = serverChannel->getConnectionToken(); - auto&& fd = serverChannel->getFd(); + const int fd = serverChannel->getFd(); + std::shared_ptr<Connection> connection = + std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/true, + mIdGenerator); - if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { + auto [_, inserted] = mConnectionsByToken.emplace(token, connection); + if (!inserted) { ALOGE("Created a new connection, but the token %p is already known", token.get()); } - mConnectionsByToken.emplace(token, connection); + std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid); + mGlobalMonitorsByDisplay[displayId].emplace_back(connection, pid); - mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr); } @@ -5948,7 +6059,7 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection removeMonitorChannelLocked(connectionToken); } - mLooper->removeFd(connection->inputChannel->getFd().get()); + mLooper->removeFd(connection->inputPublisher.getChannel().getFd()); nsecs_t currentTime = now(); abortBrokenDispatchCycleLocked(currentTime, connection, notify); @@ -5961,7 +6072,7 @@ void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionTo for (auto it = mGlobalMonitorsByDisplay.begin(); it != mGlobalMonitorsByDisplay.end();) { auto& [displayId, monitors] = *it; std::erase_if(monitors, [connectionToken](const Monitor& monitor) { - return monitor.inputChannel->getConnectionToken() == connectionToken; + return monitor.connection->getToken() == connectionToken; }); if (monitors.empty()) { @@ -5978,8 +6089,8 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { } status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { - const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token); - if (!requestingChannel) { + const std::shared_ptr<Connection> requestingConnection = getConnectionLocked(token); + if (!requestingConnection) { LOG(WARNING) << "Attempted to pilfer pointers from an un-registered channel or invalid token"; return BAD_VALUE; @@ -6006,20 +6117,20 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { "input channel stole pointer stream"); options.deviceId = deviceId; options.displayId = displayId; - std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId); + std::vector<PointerProperties> pointers = window.getTouchingPointers(deviceId); + std::bitset<MAX_POINTER_ID + 1> pointerIds = getPointerIds(pointers); options.pointerIds = pointerIds; + std::string canceledWindows; for (const TouchedWindow& w : state.windows) { - const std::shared_ptr<InputChannel> channel = - getInputChannelLocked(w.windowHandle->getToken()); - if (channel != nullptr && channel->getConnectionToken() != token) { - synthesizeCancelationEventsForInputChannelLocked(channel, options); + if (w.windowHandle->getToken() != token) { + synthesizeCancelationEventsForWindowLocked(w.windowHandle, options); canceledWindows += canceledWindows.empty() ? "[" : ", "; - canceledWindows += channel->getName(); + canceledWindows += w.windowHandle->getName(); } } canceledWindows += canceledWindows.empty() ? "[]" : "]"; - LOG(INFO) << "Channel " << requestingChannel->getName() + LOG(INFO) << "Channel " << requestingConnection->getInputChannelName() << " is stealing input gesture for device " << deviceId << " from " << canceledWindows; @@ -6085,7 +6196,7 @@ void InputDispatcher::setDisplayEligibilityForPointerCapture(int32_t displayId, std::optional<gui::Pid> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { - if (monitor.inputChannel->getConnectionToken() == token) { + if (monitor.connection->getToken() == token) { return monitor.pid; } } @@ -6117,8 +6228,8 @@ std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connecti } void InputDispatcher::removeConnectionLocked(const std::shared_ptr<Connection>& connection) { - mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); - mConnectionsByToken.erase(connection->inputChannel->getConnectionToken()); + mAnrTracker.eraseToken(connection->getToken()); + mConnectionsByToken.erase(connection->getToken()); } void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, @@ -6140,12 +6251,11 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, const nsecs_t eventDuration = finishTime - dispatchEntry.deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), + ALOGI("%s spent %" PRId64 "ms processing %s", connection->getInputChannelName().c_str(), ns2ms(eventDuration), dispatchEntry.eventEntry->getDescription().c_str()); } if (shouldReportFinishedEvent(dispatchEntry, *connection)) { - mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id, - connection->inputChannel->getConnectionToken(), + mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id, connection->getToken(), dispatchEntry.deliveryTime, consumeTime, finishTime); } @@ -6166,7 +6276,7 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, std::unique_ptr<DispatchEntry> dispatchEntry = std::move(*entryIt); connection->waitQueue.erase(entryIt); - const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken(); + const sp<IBinder>& connectionToken = connection->getToken(); mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken); if (!connection->responsive) { connection->responsive = isConnectionResponsive(*connection); @@ -6177,9 +6287,18 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } traceWaitQueueLength(*connection); if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) { - const InputTarget target{.inputChannel = connection->inputChannel, - .flags = dispatchEntry->targetFlags}; - enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target); + const auto windowHandle = getWindowHandleLocked(connection->getToken()); + // Only dispatch fallbacks if there is a window for the connection. + if (windowHandle != nullptr) { + const auto inputTarget = + createInputTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS, + dispatchEntry->targetFlags, + fallbackKeyEntry->downTime); + if (inputTarget.has_value()) { + enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), + *inputTarget); + } + } } releaseDispatchEntry(std::move(dispatchEntry)); } @@ -6213,7 +6332,7 @@ void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) // is already healthy again. Don't raise ANR in this situation if (connection->waitQueue.empty()) { ALOGI("Not raising ANR because the connection %s has recovered", - connection->inputChannel->getName().c_str()); + connection->getInputChannelName().c_str()); return; } /** @@ -6228,10 +6347,10 @@ void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) const nsecs_t currentWait = now() - oldestEntry.deliveryTime; std::string reason = android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s", - connection->inputChannel->getName().c_str(), + connection->getInputChannelName().c_str(), ns2ms(currentWait), oldestEntry.eventEntry->getDescription().c_str()); - sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken(); + sp<IBinder> connectionToken = connection->getToken(); updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason); processConnectionUnresponsiveLocked(*connection, std::move(reason)); @@ -6330,15 +6449,15 @@ void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& token */ void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection, std::string reason) { - const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken(); + const sp<IBinder>& connectionToken = connection.getToken(); std::optional<gui::Pid> pid; if (connection.monitor) { - ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(), + ALOGW("Monitor %s is unresponsive: %s", connection.getInputChannelName().c_str(), reason.c_str()); pid = findMonitorPidByTokenLocked(connectionToken); } else { // The connection is a window - ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(), + ALOGW("Window %s is unresponsive: %s", connection.getInputChannelName().c_str(), reason.c_str()); const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken); if (handle != nullptr) { @@ -6352,7 +6471,7 @@ void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& conn * Tell the policy that a connection has become responsive so that it can stop ANR. */ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) { - const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken(); + const sp<IBinder>& connectionToken = connection.getToken(); std::optional<gui::Pid> pid; if (connection.monitor) { pid = findMonitorPidByTokenLocked(connectionToken); @@ -6403,22 +6522,26 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl mLock.unlock(); if (const auto unhandledKeyFallback = - mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), - event, keyEntry.policyFlags); + mPolicy.dispatchUnhandledKey(connection->getToken(), event, + keyEntry.policyFlags); unhandledKeyFallback) { event = *unhandledKeyFallback; } mLock.lock(); - // Cancel the fallback key. + // Cancel the fallback key, but only if we still have a window for the channel. + // It could have been removed during the policy call. if (*fallbackKeyCode != AKEYCODE_UNKNOWN) { - 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"); - options.keyCode = *fallbackKeyCode; - synthesizeCancelationEventsForConnectionLocked(connection, options); + const auto windowHandle = getWindowHandleLocked(connection->getToken()); + if (windowHandle != nullptr) { + 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"); + options.keyCode = *fallbackKeyCode; + synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); + } } connection->inputState.removeFallbackKey(originalKeyCode); } @@ -6448,8 +6571,8 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl mLock.unlock(); bool fallback = false; - if (auto fb = mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), - event, keyEntry.policyFlags); + if (auto fb = mPolicy.dispatchUnhandledKey(connection->getToken(), event, + keyEntry.policyFlags); fb) { fallback = true; event = *fb; @@ -6494,10 +6617,13 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl } } - CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, - "canceling fallback, policy no longer desires it"); - options.keyCode = *fallbackKeyCode; - synthesizeCancelationEventsForConnectionLocked(connection, options); + const auto windowHandle = getWindowHandleLocked(connection->getToken()); + if (windowHandle != nullptr) { + CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, + "canceling fallback, policy no longer desires it"); + options.keyCode = *fallbackKeyCode; + synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); + } fallback = false; *fallbackKeyCode = AKEYCODE_UNKNOWN; @@ -6557,7 +6683,8 @@ void InputDispatcher::traceInboundQueueLengthLocked() { void InputDispatcher::traceOutboundQueueLength(const Connection& connection) { if (ATRACE_ENABLED()) { char counterName[40]; - snprintf(counterName, sizeof(counterName), "oq:%s", connection.getWindowName().c_str()); + snprintf(counterName, sizeof(counterName), "oq:%s", + connection.getInputChannelName().c_str()); ATRACE_INT(counterName, connection.outboundQueue.size()); } } @@ -6565,7 +6692,8 @@ void InputDispatcher::traceOutboundQueueLength(const Connection& connection) { void InputDispatcher::traceWaitQueueLength(const Connection& connection) { if (ATRACE_ENABLED()) { char counterName[40]; - snprintf(counterName, sizeof(counterName), "wq:%s", connection.getWindowName().c_str()); + snprintf(counterName, sizeof(counterName), "wq:%s", + connection.getInputChannelName().c_str()); ATRACE_INT(counterName, connection.waitQueue.size()); } } @@ -6634,15 +6762,19 @@ void InputDispatcher::setFocusedWindow(const FocusRequest& request) { mLooper->wake(); } -void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) { +void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes, + const sp<WindowInfoHandle> removedFocusedWindowHandle) { if (changes.oldFocus) { - std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus); - if (focusedInputChannel) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, - "focus left window"); - synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); - enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason); + const auto resolvedWindow = removedFocusedWindowHandle != nullptr + ? removedFocusedWindowHandle + : getWindowHandleLocked(changes.oldFocus, changes.displayId); + if (resolvedWindow == nullptr) { + LOG(FATAL) << __func__ << ": Previously focused token did not have a window"; } + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, + "focus left window"); + synthesizeCancelationEventsForWindowLocked(resolvedWindow, options); + enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason); } if (changes.newFocus) { resetNoFocusedWindowTimeoutLocked(); @@ -6723,6 +6855,15 @@ void InputDispatcher::displayRemoved(int32_t displayId) { } void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) { + if (auto result = validateWindowInfosUpdate(update); !result.ok()) { + { + // acquire lock + std::scoped_lock _l(mLock); + logDispatchStateLocked(); + } + LOG_ALWAYS_FATAL("Incorrect WindowInfosUpdate provided: %s", + result.error().message().c_str()); + }; // The listener sends the windows as a flattened array. Separate the windows by display for // more convenient parsing. std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay; @@ -6803,10 +6944,10 @@ void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanosecon void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle, const sp<WindowInfoHandle>& newWindowHandle, - TouchState& state, int32_t deviceId, int32_t pointerId, + TouchState& state, int32_t deviceId, + const PointerProperties& pointerProperties, std::vector<InputTarget>& targets) const { - std::bitset<MAX_POINTER_ID + 1> pointerIds; - pointerIds.set(pointerId); + std::vector<PointerProperties> pointers{pointerProperties}; const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); const bool newHasWallpaper = targetFlags.test(InputTarget::Flags::FOREGROUND) && @@ -6823,16 +6964,16 @@ void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFl if (oldWallpaper != nullptr) { const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper); addPointerWindowTargetLocked(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT, - oldTouchedWindow.targetFlags, pointerIds, + oldTouchedWindow.targetFlags, getPointerIds(pointers), oldTouchedWindow.getDownTimeInTarget(deviceId), targets); - state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper); + state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper); } if (newWallpaper != nullptr) { state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER, InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED, - deviceId, pointerIds); + deviceId, pointers); } } @@ -6841,7 +6982,7 @@ void InputDispatcher::transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldT const sp<WindowInfoHandle> fromWindowHandle, const sp<WindowInfoHandle> toWindowHandle, TouchState& state, int32_t deviceId, - std::bitset<MAX_POINTER_ID + 1> pointerIds) { + const std::vector<PointerProperties>& pointers) { const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) && fromWindowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); @@ -6866,11 +7007,12 @@ void InputDispatcher::transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldT if (newWallpaper != nullptr) { nsecs_t downTimeInTarget = now(); - ftl::Flags<InputTarget::Flags> wallpaperFlags = oldTargetFlags & InputTarget::Flags::SPLIT; + ftl::Flags<InputTarget::Flags> wallpaperFlags = newTargetFlags; + wallpaperFlags |= oldTargetFlags & InputTarget::Flags::SPLIT; wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, - deviceId, pointerIds, downTimeInTarget); + deviceId, pointers, downTimeInTarget); std::shared_ptr<Connection> wallpaperConnection = getConnectionLocked(newWallpaper->getToken()); if (wallpaperConnection != nullptr) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 3f99b2d40e..269bfddb8c 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -32,6 +32,8 @@ #include "Monitor.h" #include "TouchState.h" #include "TouchedWindow.h" +#include "trace/InputTracerInterface.h" +#include "trace/InputTracingBackendInterface.h" #include <attestation/HmacKeyManager.h> #include <gui/InputApplication.h> @@ -39,6 +41,7 @@ #include <input/Input.h> #include <input/InputTransport.h> #include <limits.h> +#include <powermanager/PowerManager.h> #include <stddef.h> #include <unistd.h> #include <utils/BitSet.h> @@ -82,6 +85,9 @@ public: static constexpr bool kDefaultInTouchMode = true; explicit InputDispatcher(InputDispatcherPolicyInterface& policy); + // Constructor used for testing. + explicit InputDispatcher(InputDispatcherPolicyInterface&, + std::unique_ptr<trace::InputTracingBackendInterface>); ~InputDispatcher() override; void dump(std::string& dump) const override; @@ -111,15 +117,16 @@ public: int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override; void setFocusedDisplay(int32_t displayId) override; + void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) override; void setInputDispatchMode(bool enabled, bool frozen) override; void setInputFilterEnabled(bool enabled) override; bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission, int32_t displayId) override; void setMaximumObscuringOpacityForTouch(float opacity) override; - bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop = false) override; - bool transferTouch(const sp<IBinder>& destChannelToken, int32_t displayId) override; + bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool isDragDrop = false) override; + bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken, int32_t displayId) override; base::Result<std::unique_ptr<InputChannel>> createInputChannel( const std::string& name) override; @@ -155,7 +162,6 @@ private: enum class DropReason { NOT_DROPPED, POLICY, - APP_SWITCH, DISABLED, BLOCKED, STALE, @@ -172,6 +178,9 @@ private: std::condition_variable mDispatcherIsAlive; mutable std::condition_variable mDispatcherEnteredIdle; + // Input event tracer. The tracer will only exist on builds where input tracing is allowed. + std::unique_ptr<trace::InputTracerInterface> mTracer GUARDED_BY(mLock); + sp<Looper> mLooper; std::shared_ptr<const EventEntry> mPendingEvent GUARDED_BY(mLock); @@ -204,12 +213,17 @@ private: int64_t mWindowInfosVsyncId GUARDED_BY(mLock); + std::chrono::milliseconds mMinTimeBetweenUserActivityPokes GUARDED_BY(mLock); + + /** Stores the latest user-activity poke event times per user activity types. */ + std::array<nsecs_t, USER_ACTIVITY_EVENT_LAST + 1> mLastUserActivityTimes GUARDED_BY(mLock); + // With each iteration, InputDispatcher nominally processes one queued event, // a timeout, or a response from an input consumer. // This method should only be called on the input dispatcher's own thread. void dispatchOnce(); - void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) REQUIRES(mLock); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. bool enqueueInboundEventLocked(std::unique_ptr<EventEntry> entry) REQUIRES(mLock); @@ -228,14 +242,6 @@ private: // Adds an event to a queue of recent events for debugging purposes. void addRecentEventLocked(std::shared_ptr<const EventEntry> entry) REQUIRES(mLock); - // App switch latency optimization. - bool mAppSwitchSawKeyDown GUARDED_BY(mLock); - nsecs_t mAppSwitchDueTime GUARDED_BY(mLock); - - bool isAppSwitchKeyEvent(const KeyEntry& keyEntry); - bool isAppSwitchPendingLocked() const REQUIRES(mLock); - void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock); - // Blocked event latency optimization. Drops old events when the user intends // to transfer focus to a new application. std::shared_ptr<const EventEntry> mNextUnblockedEvent GUARDED_BY(mLock); @@ -363,8 +369,6 @@ private: REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getWindowHandleLocked( const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); - std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const - REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock); bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window, @@ -435,9 +439,9 @@ private: bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry) REQUIRES(mLock); bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<const MotionEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry) REQUIRES(mLock); void dispatchPointerCaptureChangedLocked( @@ -449,7 +453,7 @@ private: void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<const EventEntry> entry, const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<const SensorEntry>& entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry) REQUIRES(mLock); void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); @@ -466,7 +470,7 @@ private: bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry); - bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); + bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) const REQUIRES(mLock); /** * Time to stop waiting for the events to be processed while trying to dispatch a key. @@ -521,7 +525,7 @@ private: int32_t getTargetDisplayId(const EventEntry& entry); sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked( - nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, + nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime, android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); std::vector<InputTarget> findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, @@ -611,21 +615,21 @@ private: REQUIRES(mLock); void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForInputChannelLocked( - const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) + void synthesizeCancelationEventsForWindowLocked(const sp<gui::WindowInfoHandle>&, + const CancelationOptions&, + const std::shared_ptr<Connection>& = nullptr) REQUIRES(mLock); + // This is a convenience function used to generate cancellation for a connection without having + // to check whether it's a monitor or a window. For non-monitors, the window handle must not be + // null. Always prefer the "-ForWindow" method above when explicitly dealing with windows. void synthesizeCancelationEventsForConnectionLocked( - const std::shared_ptr<Connection>& connection, const CancelationOptions& options) - REQUIRES(mLock); + const std::shared_ptr<Connection>& connection, const CancelationOptions& options, + const sp<gui::WindowInfoHandle>& window) REQUIRES(mLock); void synthesizePointerDownEventsForConnectionLocked( const nsecs_t downTime, const std::shared_ptr<Connection>& connection, ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock); - void synthesizeCancelationEventsForWindowLocked( - const sp<android::gui::WindowInfoHandle>& windowHandle, - const CancelationOptions& options) 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 std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry, @@ -652,7 +656,9 @@ private: bool handled, nsecs_t consumeTime) REQUIRES(mLock); void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken, const KeyEntry& entry) REQUIRES(mLock); - void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock); + void onFocusChangedLocked(const FocusResolver::FocusChanges& changes, + const sp<gui::WindowInfoHandle> removedFocusedWindowHandle = nullptr) + REQUIRES(mLock); void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) REQUIRES(mLock); void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock); @@ -690,14 +696,15 @@ private: void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, const sp<android::gui::WindowInfoHandle>& oldWindowHandle, const sp<android::gui::WindowInfoHandle>& newWindowHandle, - TouchState& state, int32_t deviceId, int32_t pointerId, + TouchState& state, int32_t deviceId, + const PointerProperties& pointerProperties, std::vector<InputTarget>& targets) const REQUIRES(mLock); void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags, ftl::Flags<InputTarget::Flags> newTargetFlags, const sp<android::gui::WindowInfoHandle> fromWindowHandle, const sp<android::gui::WindowInfoHandle> toWindowHandle, TouchState& state, int32_t deviceId, - std::bitset<MAX_POINTER_ID + 1> pointerIds) REQUIRES(mLock); + const std::vector<PointerProperties>& pointers) REQUIRES(mLock); sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow( const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index a4ac4fbe6c..1fec9b7599 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -195,6 +195,16 @@ bool InputState::trackMotion(const MotionEntry& entry, int32_t flags) { } } +std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>> +InputState::getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const { + ssize_t index = findMotionMemento(entry, hovering); + if (index == -1) { + return std::nullopt; + } + return std::make_pair(mMotionMementos[index].pointerProperties, + mMotionMementos[index].pointerCoords); +} + ssize_t InputState::findKeyMemento(const KeyEntry& entry) const { for (size_t i = 0; i < mKeyMementos.size(); i++) { const KeyMemento& memento = mKeyMementos[i]; @@ -311,16 +321,6 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry) cons return true; } - // Use the previous stream cancellation logic to generate all HOVER_EXIT events. - // If this hover event was generated as a result of the pointer leaving the window, - // the HOVER_EXIT event should have the same coordinates as the previous - // HOVER_MOVE event in this stream. Ensure that all HOVER_EXITs have the same - // coordinates as the previous event by cancelling the stream here. With this approach, the - // HOVER_EXIT event is generated from the previous event. - if (actionMasked == AMOTION_EVENT_ACTION_HOVER_EXIT && lastMemento.hovering) { - return true; - } - // If the stream changes its source, just cancel the current gesture to be safe. It's // possible that the app isn't handling source changes properly if (motionEntry.source != lastMemento.source) { diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index b0e4209882..d49469ddc1 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -48,6 +48,15 @@ public: // and should be skipped. bool trackMotion(const MotionEntry& entry, int32_t flags); + /** + * Return the PointerProperties and the PointerCoords for the last event, if found. Return + * std::nullopt if not found. We should not return std::vector<PointerCoords> in isolation, + * because the pointers can technically be stored in the vector in any order, so the + * PointerProperties are needed to specify the order in which the pointer coords are stored. + */ + std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>> + getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const; + // Create cancel events for the previous stream if the current motionEntry requires it. std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry); diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index c02c5d67a2..35ad858736 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -26,6 +26,15 @@ using android::base::StringPrintf; namespace android::inputdispatcher { +namespace { + +const static ui::Transform kIdentityTransform{}; + +} + +InputTarget::InputTarget(const std::shared_ptr<Connection>& connection, ftl::Flags<Flags> flags) + : connection(connection), flags(flags) {} + void InputTarget::addPointers(std::bitset<MAX_POINTER_ID + 1> newPointerIds, const ui::Transform& transform) { // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no @@ -36,31 +45,46 @@ void InputTarget::addPointers(std::bitset<MAX_POINTER_ID + 1> newPointerIds, } // Ensure that the new set of pointers doesn't overlap with the current set of pointers. - if ((pointerIds & newPointerIds).any()) { + if ((getPointerIds() & newPointerIds).any()) { LOG(FATAL) << __func__ << " - overlap with incoming pointers " << bitsetToString(newPointerIds) << " in " << *this; } - pointerIds |= newPointerIds; - for (size_t i = 0; i < newPointerIds.size(); i++) { - if (!newPointerIds.test(i)) { - continue; + for (auto& [existingTransform, existingPointers] : mPointerTransforms) { + if (transform == existingTransform) { + existingPointers |= newPointerIds; + return; } - pointerTransforms[i] = transform; } + mPointerTransforms.emplace_back(transform, newPointerIds); } void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) { - pointerIds.reset(); - pointerTransforms[0] = transform; + mPointerTransforms = {{transform, {}}}; } bool InputTarget::useDefaultPointerTransform() const { - return pointerIds.none(); + return mPointerTransforms.size() <= 1; } const ui::Transform& InputTarget::getDefaultPointerTransform() const { - return pointerTransforms[0]; + if (!useDefaultPointerTransform()) { + LOG(FATAL) << __func__ << ": Not using default pointer transform"; + } + return mPointerTransforms.size() == 1 ? mPointerTransforms[0].first : kIdentityTransform; +} + +const ui::Transform& InputTarget::getTransformForPointer(int32_t pointerId) const { + for (const auto& [transform, ids] : mPointerTransforms) { + if (ids.test(pointerId)) { + return transform; + } + } + + LOG(FATAL) << __func__ + << ": Cannot get transform: The following Pointer ID does not exist in target: " + << pointerId; + return kIdentityTransform; } std::string InputTarget::getPointerInfoString() const { @@ -71,21 +95,25 @@ std::string InputTarget::getPointerInfoString() const { return out; } - for (uint32_t i = 0; i < pointerIds.size(); i++) { - if (!pointerIds.test(i)) { - continue; - } - - const std::string name = "pointerId " + std::to_string(i) + ":"; - pointerTransforms[i].dump(out, name.c_str(), " "); + for (const auto& [transform, ids] : mPointerTransforms) { + const std::string name = "pointerIds " + bitsetToString(ids) + ":"; + transform.dump(out, name.c_str(), " "); } return out; } +std::bitset<MAX_POINTER_ID + 1> InputTarget::getPointerIds() const { + PointerIds allIds; + for (const auto& [_, ids] : mPointerTransforms) { + allIds |= ids; + } + return allIds; +} + std::ostream& operator<<(std::ostream& out, const InputTarget& target) { - out << "{inputChannel="; - if (target.inputChannel != nullptr) { - out << target.inputChannel->getName(); + out << "{connection="; + if (target.connection != nullptr) { + out << target.connection->getInputChannelName(); } else { out << "<null>"; } diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index aef866baaf..058639a742 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -19,10 +19,11 @@ #include <ftl/flags.h> #include <gui/WindowInfo.h> #include <gui/constants.h> -#include <input/InputTransport.h> #include <ui/Transform.h> #include <utils/BitSet.h> #include <bitset> +#include "Connection.h" +#include "InputTargetFlags.h" namespace android::inputdispatcher { @@ -32,30 +33,9 @@ namespace android::inputdispatcher { * be added to input event coordinates to compensate for the absolute position of the * window area. */ -struct InputTarget { - enum class Flags : uint32_t { - /* This flag indicates that the event is being delivered to a foreground application. */ - FOREGROUND = 1 << 0, - - /* This flag indicates that the MotionEvent falls within the area of the target - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - WINDOW_IS_OBSCURED = 1 << 1, - - /* This flag indicates that a motion event is being split across multiple windows. */ - SPLIT = 1 << 2, - - /* This flag indicates that the pointer coordinates dispatched to the application - * will be zeroed out to avoid revealing information to an application. This is - * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing - * the same UID from watching all touches. */ - ZERO_COORDS = 1 << 3, - - /* This flag indicates that the target of a MotionEvent is partly or wholly - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ - WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, - }; +class InputTarget { +public: + using Flags = InputTargetFlags; enum class DispatchMode { /* This flag indicates that the event should be sent as is. @@ -85,8 +65,8 @@ struct InputTarget { ftl_last = SLIPPERY_ENTER, }; - // The input channel to be targeted. - std::shared_ptr<InputChannel> inputChannel; + // The input connection to be targeted. + std::shared_ptr<Connection> connection; // Flags for the input target. ftl::Flags<Flags> flags; @@ -101,20 +81,17 @@ struct InputTarget { // Current display transform. Used for compatibility for raw coordinates. ui::Transform displayTransform; - // The subset of pointer ids to include in motion events dispatched to this input target - // if FLAG_SPLIT is set. - std::bitset<MAX_POINTER_ID + 1> pointerIds; // Event time for the first motion event (ACTION_DOWN) dispatched to this input target if // FLAG_SPLIT is set. std::optional<nsecs_t> firstDownTimeInTarget; - // The data is stored by the pointerId. Use the bit position of pointerIds to look up - // Transform per pointerId. - ui::Transform pointerTransforms[MAX_POINTERS]; // The window that this input target is being dispatched to. It is possible for this to be // null for cases like global monitors. sp<gui::WindowInfoHandle> windowHandle; + InputTarget() = default; + InputTarget(const std::shared_ptr<Connection>&, ftl::Flags<Flags> = {}); + void addPointers(std::bitset<MAX_POINTER_ID + 1> pointerIds, const ui::Transform& transform); void setDefaultPointerTransform(const ui::Transform& transform); @@ -132,7 +109,22 @@ struct InputTarget { */ const ui::Transform& getDefaultPointerTransform() const; + const ui::Transform& getTransformForPointer(int32_t pointerId) const; + + std::bitset<MAX_POINTER_ID + 1> getPointerIds() const; + std::string getPointerInfoString() const; + +private: + template <typename K, typename V> + using ArrayMap = std::vector<std::pair<K, V>>; + using PointerIds = std::bitset<MAX_POINTER_ID + 1>; + // The mapping of pointer IDs to the transform that should be used for that collection of IDs. + // Each of the pointer IDs are mutually disjoint, and their union makes up pointer IDs to + // include in the motion events dispatched to this target. We use an ArrayMap to store this to + // avoid having to define hash or comparison functions for ui::Transform, which would be needed + // to use std::unordered_map or std::map respectively. + ArrayMap<ui::Transform, PointerIds> mPointerTransforms; }; std::ostream& operator<<(std::ostream& out, const InputTarget& target); diff --git a/services/inputflinger/dispatcher/InputTargetFlags.h b/services/inputflinger/dispatcher/InputTargetFlags.h new file mode 100644 index 0000000000..efebb18144 --- /dev/null +++ b/services/inputflinger/dispatcher/InputTargetFlags.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 <ftl/flags.h> + +namespace android::inputdispatcher { + +enum class InputTargetFlags : uint32_t { + /* This flag indicates that the event is being delivered to a foreground application. */ + FOREGROUND = 1 << 0, + + /* This flag indicates that the MotionEvent falls within the area of the target + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ + WINDOW_IS_OBSCURED = 1 << 1, + + /* This flag indicates that a motion event is being split across multiple windows. */ + SPLIT = 1 << 2, + + /* This flag indicates that the pointer coordinates dispatched to the application + * will be zeroed out to avoid revealing information to an application. This is + * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing + * the same UID from watching all touches. */ + ZERO_COORDS = 1 << 3, + + /* This flag indicates that the event will not cause a focus change if it is directed to an + * unfocused window, even if it an ACTION_DOWN. This is typically used to allow gestures to be + * directed to an unfocused window without bringing it into focus. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE. */ + NO_FOCUS_CHANGE = 1 << 4, + + /* This flag indicates that the target of a MotionEvent is partly or wholly + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ + WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, +}; + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp index 204791eb03..4e77d905aa 100644 --- a/services/inputflinger/dispatcher/Monitor.cpp +++ b/services/inputflinger/dispatcher/Monitor.cpp @@ -19,7 +19,7 @@ namespace android::inputdispatcher { // --- Monitor --- -Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid) - : inputChannel(inputChannel), pid(pid) {} +Monitor::Monitor(const std::shared_ptr<Connection>& connection, gui::Pid pid) + : connection(connection), pid(pid) {} } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h index 1b1eb3a593..d15a222752 100644 --- a/services/inputflinger/dispatcher/Monitor.h +++ b/services/inputflinger/dispatcher/Monitor.h @@ -17,16 +17,16 @@ #pragma once #include <gui/PidUid.h> -#include <input/InputTransport.h> +#include "Connection.h" namespace android::inputdispatcher { struct Monitor { - std::shared_ptr<InputChannel> inputChannel; // never null + std::shared_ptr<Connection> connection; // never null gui::Pid pid; - explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid); + explicit Monitor(const std::shared_ptr<Connection>& connection, gui::Pid pid); }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index e8d8c18e4e..f8aa62500e 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -73,9 +73,9 @@ void TouchState::clearWindowsWithoutPointers() { void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, - std::bitset<MAX_POINTER_ID + 1> touchingPointerIds, + const std::vector<PointerProperties>& touchingPointers, std::optional<nsecs_t> firstDownTimeInTarget) { - if (touchingPointerIds.none()) { + if (touchingPointers.empty()) { LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName(); return; } @@ -91,7 +91,7 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have // downTime set initially. Need to update existing window when a pointer is down for the // window. - touchedWindow.addTouchingPointers(deviceId, touchingPointerIds); + touchedWindow.addTouchingPointers(deviceId, touchingPointers); if (firstDownTimeInTarget) { touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget); } @@ -102,7 +102,7 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, touchedWindow.windowHandle = windowHandle; touchedWindow.dispatchMode = dispatchMode; touchedWindow.targetFlags = targetFlags; - touchedWindow.addTouchingPointers(deviceId, touchingPointerIds); + touchedWindow.addTouchingPointers(deviceId, touchingPointers); if (firstDownTimeInTarget) { touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget); } @@ -110,17 +110,17 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, } void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle, - DeviceId deviceId, int32_t hoveringPointerId) { + DeviceId deviceId, const PointerProperties& pointer) { for (TouchedWindow& touchedWindow : windows) { if (touchedWindow.windowHandle == windowHandle) { - touchedWindow.addHoveringPointer(deviceId, hoveringPointerId); + touchedWindow.addHoveringPointer(deviceId, pointer); return; } } TouchedWindow touchedWindow; touchedWindow.windowHandle = windowHandle; - touchedWindow.addHoveringPointer(deviceId, hoveringPointerId); + touchedWindow.addHoveringPointer(deviceId, pointer); windows.push_back(touchedWindow); } @@ -234,6 +234,11 @@ bool TouchState::hasHoveringPointers(DeviceId deviceId) const { }); } +bool TouchState::hasActiveStylus() const { + return std::any_of(windows.begin(), windows.end(), + [](const TouchedWindow& window) { return window.hasActiveStylus(); }); +} + std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId, int32_t pointerId) const { std::set<sp<WindowInfoHandle>> out; diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index e0a84e8f45..3d534bc71d 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -46,11 +46,11 @@ struct TouchState { void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, - std::bitset<MAX_POINTER_ID + 1> touchingPointerIds, + const std::vector<PointerProperties>& touchingPointers, std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt); void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, - DeviceId deviceId, int32_t hoveringPointerId); - void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId); + DeviceId deviceId, const PointerProperties& pointer); + void removeHoveringPointer(DeviceId deviceId, int32_t pointerId); void clearHoveringPointers(DeviceId deviceId); void removeAllPointersForDevice(DeviceId deviceId); @@ -73,6 +73,8 @@ struct TouchState { bool isDown(DeviceId deviceId) const; bool hasHoveringPointers(DeviceId deviceId) const; + bool hasActiveStylus() const; + std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer( DeviceId deviceId, int32_t pointerId) const; std::string dump() const; diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp index cd0500c872..037d7c8e99 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.cpp +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -26,9 +26,20 @@ namespace android { namespace inputdispatcher { +namespace { + +bool hasPointerId(const std::vector<PointerProperties>& pointers, int32_t pointerId) { + return std::find_if(pointers.begin(), pointers.end(), + [&pointerId](const PointerProperties& properties) { + return properties.id == pointerId; + }) != pointers.end(); +} + +} // namespace + bool TouchedWindow::hasHoveringPointers() const { for (const auto& [_, state] : mDeviceStates) { - if (state.hoveringPointerIds.any()) { + if (!state.hoveringPointers.empty()) { return true; } } @@ -42,7 +53,7 @@ bool TouchedWindow::hasHoveringPointers(DeviceId deviceId) const { } const DeviceState& state = stateIt->second; - return state.hoveringPointerIds.any(); + return !state.hoveringPointers.empty(); } void TouchedWindow::clearHoveringPointers(DeviceId deviceId) { @@ -51,7 +62,7 @@ void TouchedWindow::clearHoveringPointers(DeviceId deviceId) { return; } DeviceState& state = stateIt->second; - state.hoveringPointerIds.reset(); + state.hoveringPointers.clear(); if (!state.hasPointers()) { mDeviceStates.erase(stateIt); } @@ -63,22 +74,40 @@ bool TouchedWindow::hasHoveringPointer(DeviceId deviceId, int32_t pointerId) con return false; } const DeviceState& state = stateIt->second; - - return state.hoveringPointerIds.test(pointerId); + return hasPointerId(state.hoveringPointers, pointerId); } -void TouchedWindow::addHoveringPointer(DeviceId deviceId, int32_t pointerId) { - mDeviceStates[deviceId].hoveringPointerIds.set(pointerId); +void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer) { + std::vector<PointerProperties>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers; + const size_t initialSize = hoveringPointers.size(); + std::erase_if(hoveringPointers, [&pointer](const PointerProperties& properties) { + return properties.id == pointer.id; + }); + if (hoveringPointers.size() != initialSize) { + LOG(ERROR) << __func__ << ": " << pointer << ", device " << deviceId << " was in " << *this; + } + hoveringPointers.push_back(pointer); } void TouchedWindow::addTouchingPointers(DeviceId deviceId, - std::bitset<MAX_POINTER_ID + 1> pointers) { - mDeviceStates[deviceId].touchingPointerIds |= pointers; + const std::vector<PointerProperties>& pointers) { + std::vector<PointerProperties>& touchingPointers = mDeviceStates[deviceId].touchingPointers; + const size_t initialSize = touchingPointers.size(); + for (const PointerProperties& pointer : pointers) { + std::erase_if(touchingPointers, [&pointer](const PointerProperties& properties) { + return properties.id == pointer.id; + }); + } + if (touchingPointers.size() != initialSize) { + LOG(ERROR) << __func__ << ": " << dumpVector(pointers, streamableToString) << ", device " + << deviceId << " already in " << *this; + } + touchingPointers.insert(touchingPointers.end(), pointers.begin(), pointers.end()); } bool TouchedWindow::hasTouchingPointers() const { for (const auto& [_, state] : mDeviceStates) { - if (state.touchingPointerIds.any()) { + if (!state.touchingPointers.empty()) { return true; } } @@ -86,21 +115,25 @@ bool TouchedWindow::hasTouchingPointers() const { } bool TouchedWindow::hasTouchingPointers(DeviceId deviceId) const { - return getTouchingPointers(deviceId).any(); + return !getTouchingPointers(deviceId).empty(); } bool TouchedWindow::hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const { - return getTouchingPointers(deviceId).test(pointerId); + const auto stateIt = mDeviceStates.find(deviceId); + if (stateIt == mDeviceStates.end()) { + return false; + } + const DeviceState& state = stateIt->second; + return hasPointerId(state.touchingPointers, pointerId); } -std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getTouchingPointers(DeviceId deviceId) const { +std::vector<PointerProperties> TouchedWindow::getTouchingPointers(DeviceId deviceId) const { const auto stateIt = mDeviceStates.find(deviceId); if (stateIt == mDeviceStates.end()) { return {}; } const DeviceState& state = stateIt->second; - - return state.touchingPointerIds; + return state.touchingPointers; } void TouchedWindow::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) { @@ -118,7 +151,10 @@ void TouchedWindow::removeTouchingPointers(DeviceId deviceId, } DeviceState& state = stateIt->second; - state.touchingPointerIds &= ~pointers; + std::erase_if(state.touchingPointers, [&pointers](const PointerProperties& properties) { + return pointers.test(properties.id); + }); + state.pilferingPointerIds &= ~pointers; if (!state.hasPointers()) { @@ -126,10 +162,26 @@ void TouchedWindow::removeTouchingPointers(DeviceId deviceId, } } +bool TouchedWindow::hasActiveStylus() const { + for (const auto& [_, state] : mDeviceStates) { + for (const PointerProperties& properties : state.touchingPointers) { + if (properties.toolType == ToolType::STYLUS) { + return true; + } + } + for (const PointerProperties& properties : state.hoveringPointers) { + if (properties.toolType == ToolType::STYLUS) { + return true; + } + } + } + return false; +} + std::set<DeviceId> TouchedWindow::getTouchingDeviceIds() const { std::set<DeviceId> deviceIds; for (const auto& [deviceId, deviceState] : mDeviceStates) { - if (deviceState.touchingPointerIds.any()) { + if (!deviceState.touchingPointers.empty()) { deviceIds.insert(deviceId); } } @@ -198,7 +250,7 @@ void TouchedWindow::removeAllTouchingPointersForDevice(DeviceId deviceId) { } DeviceState& state = stateIt->second; - state.touchingPointerIds.reset(); + state.touchingPointers.clear(); state.pilferingPointerIds.reset(); state.downTimeInTarget.reset(); @@ -214,7 +266,9 @@ void TouchedWindow::removeHoveringPointer(DeviceId deviceId, int32_t pointerId) } DeviceState& state = stateIt->second; - state.hoveringPointerIds.set(pointerId, false); + std::erase_if(state.hoveringPointers, [&pointerId](const PointerProperties& properties) { + return properties.id == pointerId; + }); if (!state.hasPointers()) { mDeviceStates.erase(stateIt); @@ -228,7 +282,7 @@ void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) { } DeviceState& state = stateIt->second; - state.hoveringPointerIds.reset(); + state.hoveringPointers.clear(); if (!state.hasPointers()) { mDeviceStates.erase(stateIt); @@ -236,11 +290,11 @@ void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) { } std::string TouchedWindow::deviceStateToString(const TouchedWindow::DeviceState& state) { - return StringPrintf("[touchingPointerIds=%s, " - "downTimeInTarget=%s, hoveringPointerIds=%s, pilferingPointerIds=%s]", - bitsetToString(state.touchingPointerIds).c_str(), + return StringPrintf("[touchingPointers=%s, " + "downTimeInTarget=%s, hoveringPointers=%s, pilferingPointerIds=%s]", + dumpVector(state.touchingPointers, streamableToString).c_str(), toString(state.downTimeInTarget).c_str(), - bitsetToString(state.hoveringPointerIds).c_str(), + dumpVector(state.hoveringPointers, streamableToString).c_str(), bitsetToString(state.pilferingPointerIds).c_str()); } diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index c604353593..0d1531f8ff 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -38,17 +38,18 @@ struct TouchedWindow { bool hasHoveringPointers() const; bool hasHoveringPointers(DeviceId deviceId) const; bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const; - void addHoveringPointer(DeviceId deviceId, int32_t pointerId); + void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer); void removeHoveringPointer(DeviceId deviceId, int32_t pointerId); // Touching bool hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const; bool hasTouchingPointers() const; bool hasTouchingPointers(DeviceId deviceId) const; - std::bitset<MAX_POINTER_ID + 1> getTouchingPointers(DeviceId deviceId) const; - void addTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers); + std::vector<PointerProperties> getTouchingPointers(DeviceId deviceId) const; + void addTouchingPointers(DeviceId deviceId, const std::vector<PointerProperties>& pointers); void removeTouchingPointer(DeviceId deviceId, int32_t pointerId); void removeTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers); + bool hasActiveStylus() const; std::set<DeviceId> getTouchingDeviceIds() const; // Pilfering pointers @@ -69,16 +70,16 @@ struct TouchedWindow { private: struct DeviceState { - std::bitset<MAX_POINTER_ID + 1> touchingPointerIds; + std::vector<PointerProperties> touchingPointers; // The pointer ids of the pointers that this window is currently pilfering, by device std::bitset<MAX_POINTER_ID + 1> pilferingPointerIds; // Time at which the first action down occurred on this window, for each device // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE // scenario. std::optional<nsecs_t> downTimeInTarget; - std::bitset<MAX_POINTER_ID + 1> hoveringPointerIds; + std::vector<PointerProperties> hoveringPointers; - bool hasPointers() const { return touchingPointerIds.any() || hoveringPointerIds.any(); }; + bool hasPointers() const { return !touchingPointers.empty() || !hoveringPointers.empty(); }; }; std::map<DeviceId, DeviceState> mDeviceStates; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 001dc6cf7b..7c440e8218 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -101,6 +101,9 @@ public: */ virtual void setFocusedDisplay(int32_t displayId) = 0; + /** Sets the minimum time between user activity pokes. */ + virtual void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) = 0; + /* Sets the input dispatching mode. * * This method may be called on any thread (usually by the input manager). @@ -137,19 +140,23 @@ public: */ virtual void setMaximumObscuringOpacityForTouch(float opacity) = 0; - /* Transfers touch focus from one window to another window. + /** + * Transfers a touch gesture from one window to another window. Transferring touch will not + * have any effect on the focused window. * - * Returns true on success. False if the window did not actually have touch focus. + * Returns true on success. False if the window did not actually have an active touch gesture. */ - virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop) = 0; + virtual bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool isDragDrop) = 0; /** - * Transfer touch focus to the provided channel, no matter where the current touch is. + * Transfer a touch gesture to the provided channel, no matter where the current touch is. + * Transferring touch will not have any effect on the focused window. * - * Return true on success, false if there was no on-going touch. + * Returns true on success, false if there was no on-going touch on the display. + * @deprecated */ - virtual bool transferTouch(const sp<IBinder>& destChannelToken, int32_t displayId) = 0; + virtual bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken, int32_t displayId) = 0; /** * Sets focus on the specified window. diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 1c23720ecb..62c2b02967 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -99,8 +99,8 @@ public: * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event * should be dispatched to applications. */ - virtual void interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when, - uint32_t& policyFlags) = 0; + virtual void interceptMotionBeforeQueueing(int32_t displayId, uint32_t source, int32_t action, + nsecs_t when, uint32_t& policyFlags) = 0; /* Allows the policy a chance to intercept a key before dispatching. */ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, @@ -131,6 +131,18 @@ public: return std::chrono::nanoseconds(currentTime - eventTime) >= STALE_EVENT_TIMEOUT; } + /** + * Get the additional latency to add while waiting for other input events to process before + * dispatching the pending key. + * If there are unprocessed events, the pending key will not be dispatched immediately. Instead, + * the dispatcher will wait for this timeout, to account for the possibility that the focus + * might change due to touch or other events (such as another app getting launched by keys). + * This would give the pending key the opportunity to go to a newly focused window instead. + */ + virtual std::chrono::nanoseconds getKeyWaitingForEventsTimeout() { + return KEY_WAITING_FOR_EVENTS_TIMEOUT; + } + /* Notifies the policy that a pointer down event has occurred outside the current focused * window. * @@ -150,6 +162,13 @@ public: /* Notifies the policy that there was an input device interaction with apps. */ virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp, const std::set<gui::Uid>& uids) = 0; + +private: + // Additional key latency in case a connection is still processing some motion events. + // This will help with the case when a user touched a button that opens a new window, + // and gives us the chance to dispatch the key to this new window. + static constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = + std::chrono::milliseconds(500); }; } // namespace android diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp new file mode 100644 index 0000000000..a61fa85d6e --- /dev/null +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp @@ -0,0 +1,109 @@ +/* + * 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 "AndroidInputEventProtoConverter.h" + +#include <android-base/logging.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> + +namespace android::inputdispatcher::trace { + +void AndroidInputEventProtoConverter::toProtoMotionEvent(const TracedMotionEvent& event, + proto::AndroidMotionEvent& outProto) { + outProto.set_event_id(event.id); + outProto.set_event_time_nanos(event.eventTime); + outProto.set_down_time_nanos(event.downTime); + outProto.set_source(event.source); + outProto.set_action(event.action); + outProto.set_device_id(event.deviceId); + outProto.set_display_id(event.displayId); + outProto.set_classification(static_cast<int32_t>(event.classification)); + outProto.set_cursor_position_x(event.xCursorPosition); + outProto.set_cursor_position_y(event.yCursorPosition); + outProto.set_flags(event.flags); + outProto.set_policy_flags(event.policyFlags); + + for (uint32_t i = 0; i < event.pointerProperties.size(); i++) { + auto* pointer = outProto.add_pointer(); + + const auto& props = event.pointerProperties[i]; + pointer->set_pointer_id(props.id); + pointer->set_tool_type(static_cast<int32_t>(props.toolType)); + + const auto& coords = event.pointerCoords[i]; + auto bits = BitSet64(coords.bits); + for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { + const auto axis = bits.clearFirstMarkedBit(); + auto axisEntry = pointer->add_axis_value(); + axisEntry->set_axis(axis); + axisEntry->set_value(coords.values[axisIndex]); + } + } +} + +void AndroidInputEventProtoConverter::toProtoKeyEvent(const TracedKeyEvent& event, + proto::AndroidKeyEvent& outProto) { + outProto.set_event_id(event.id); + outProto.set_event_time_nanos(event.eventTime); + outProto.set_down_time_nanos(event.downTime); + outProto.set_source(event.source); + outProto.set_action(event.action); + outProto.set_device_id(event.deviceId); + outProto.set_display_id(event.displayId); + outProto.set_key_code(event.keyCode); + outProto.set_scan_code(event.scanCode); + outProto.set_meta_state(event.metaState); + outProto.set_repeat_count(event.repeatCount); + outProto.set_flags(event.flags); + outProto.set_policy_flags(event.policyFlags); +} + +void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent( + const InputTracingBackendInterface::WindowDispatchArgs& args, + proto::AndroidWindowInputDispatchEvent& outProto) { + std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry); + outProto.set_vsync_id(args.vsyncId); + outProto.set_window_id(args.windowId); + outProto.set_resolved_flags(args.resolvedFlags); + + if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) { + for (size_t i = 0; i < motion->pointerProperties.size(); i++) { + auto* pointerProto = outProto.add_dispatched_pointer(); + pointerProto->set_pointer_id(motion->pointerProperties[i].id); + const auto rawXY = + MotionEvent::calculateTransformedXY(motion->source, args.rawTransform, + motion->pointerCoords[i].getXYValue()); + pointerProto->set_x_in_display(rawXY.x); + pointerProto->set_y_in_display(rawXY.y); + + const auto& coords = motion->pointerCoords[i]; + const auto coordsInWindow = + MotionEvent::calculateTransformedCoords(motion->source, args.transform, coords); + auto bits = BitSet64(coords.bits); + for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { + const uint32_t axis = bits.clearFirstMarkedBit(); + const float axisValueInWindow = coordsInWindow.values[axisIndex]; + if (coords.values[axisIndex] != axisValueInWindow) { + auto* axisEntry = pointerProto->add_axis_value_in_window(); + axisEntry->set_axis(axis); + axisEntry->set_value(axisValueInWindow); + } + } + } + } +} + +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h new file mode 100644 index 0000000000..8a46f1518b --- /dev/null +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h @@ -0,0 +1,39 @@ +/* + * 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 <perfetto/trace/android/android_input_event.pbzero.h> + +#include "InputTracingBackendInterface.h" + +namespace proto = perfetto::protos::pbzero; + +namespace android::inputdispatcher::trace { + +/** + * Write traced events into Perfetto protos. + */ +class AndroidInputEventProtoConverter { +public: + static void toProtoMotionEvent(const TracedMotionEvent& event, + proto::AndroidMotionEvent& outProto); + static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto); + static void toProtoWindowDispatchEvent(const InputTracingBackendInterface::WindowDispatchArgs&, + proto::AndroidWindowInputDispatchEvent& outProto); +}; + +} // namespace android::inputdispatcher::trace diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/inputflinger/dispatcher/trace/EventTrackerInterface.h index b48529f4ac..929820e0a6 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h +++ b/services/inputflinger/dispatcher/trace/EventTrackerInterface.h @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * 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. @@ -16,17 +16,17 @@ #pragma once -#include <gmock/gmock.h> +namespace android::inputdispatcher::trace { -#include "Scheduler/VSyncTracker.h" - -namespace android::scheduler::mock { - -struct VsyncTrackerCallback final : IVsyncTrackerCallback { - MOCK_METHOD(void, onVsyncGenerated, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), (override)); +/** + * A tracker used to track the lifecycle of a traced input event. + * The tracker should be stored inside the traced event. When the event goes out of scope after + * the dispatcher has finished processing it, the tracker will call back into the tracer to + * initiate cleanup. + */ +class EventTrackerInterface { +public: + virtual ~EventTrackerInterface() = default; }; -struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback { - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override{}; -}; -} // namespace android::scheduler::mock +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp new file mode 100644 index 0000000000..be09013a83 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp @@ -0,0 +1,163 @@ +/* + * 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 LOG_TAG "InputTracer" + +#include "InputTracer.h" + +#include <android-base/logging.h> + +namespace android::inputdispatcher::trace::impl { + +namespace { + +// Helper to std::visit with lambdas. +template <typename... V> +struct Visitor : V... { + using V::operator()...; +}; + +TracedEvent createTracedEvent(const MotionEntry& e) { + return TracedMotionEvent{e.id, + e.eventTime, + e.policyFlags, + e.deviceId, + e.source, + e.displayId, + e.action, + e.actionButton, + e.flags, + e.metaState, + e.buttonState, + e.classification, + e.edgeFlags, + e.xPrecision, + e.yPrecision, + e.xCursorPosition, + e.yCursorPosition, + e.downTime, + e.pointerProperties, + e.pointerCoords}; +} + +TracedEvent createTracedEvent(const KeyEntry& e) { + return TracedKeyEvent{e.id, e.eventTime, e.policyFlags, e.deviceId, e.source, + e.displayId, e.action, e.keyCode, e.scanCode, e.metaState, + e.downTime, e.flags, e.repeatCount}; +} + +} // namespace + +// --- InputTracer --- + +InputTracer::InputTracer(std::unique_ptr<InputTracingBackendInterface> backend) + : mBackend(std::move(backend)) {} + +std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) { + TracedEvent traced; + + if (entry.type == EventEntry::Type::MOTION) { + const auto& motion = static_cast<const MotionEntry&>(entry); + traced = createTracedEvent(motion); + } else if (entry.type == EventEntry::Type::KEY) { + const auto& key = static_cast<const KeyEntry&>(entry); + traced = createTracedEvent(key); + } else { + LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type); + } + + return std::make_unique<EventTrackerImpl>(*this, std::move(traced)); +} + +void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie, + const InputTarget& target) { + auto& cookieState = getState(cookie); + if (!cookieState) { + LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()"; + } + // TODO(b/210460522): Determine if the event is sensitive based on the target. +} + +void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) { + auto& cookieState = getState(cookie); + if (!cookieState) { + LOG(FATAL) << "Traced event was already logged. " + "eventProcessingComplete() was likely called more than once."; + } + + std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); }, + [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }}, + cookieState->event); + cookieState.reset(); +} + +void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry, + const EventTrackerInterface* cookie) { + const EventEntry& entry = *dispatchEntry.eventEntry; + + TracedEvent traced; + if (entry.type == EventEntry::Type::MOTION) { + const auto& motion = static_cast<const MotionEntry&>(entry); + traced = createTracedEvent(motion); + } else if (entry.type == EventEntry::Type::KEY) { + const auto& key = static_cast<const KeyEntry&>(entry); + traced = createTracedEvent(key); + } else { + 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); + } + + // 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=*/{}}); +} + +std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) { + return static_cast<const EventTrackerImpl&>(cookie).mState; +} + +// --- InputTracer::EventTrackerImpl --- + +InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event) + : mTracer(tracer), mState(event) {} + +InputTracer::EventTrackerImpl::~EventTrackerImpl() { + if (!mState) { + // This event has already been written to the trace as expected. + return; + } + // We're still holding on to the state, which means it hasn't yet been written to the trace. + // Write it to the trace 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.reset(); +} + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h new file mode 100644 index 0000000000..c8b25c9961 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracer.h @@ -0,0 +1,79 @@ +/* + * 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 "InputTracerInterface.h" + +#include <memory> + +#include "../Entry.h" +#include "InputTracingBackendInterface.h" + +namespace android::inputdispatcher::trace::impl { + +/** + * The tracer implementation for InputDispatcher. + * + * InputTracer's responsibility is to keep track of events as they are processed by InputDispatcher, + * and to write the events to the tracing backend when enough information is collected. InputTracer + * is not thread-safe. + * + * See the documentation in InputTracerInterface for the API surface. + */ +class InputTracer : public InputTracerInterface { +public: + explicit InputTracer(std::unique_ptr<InputTracingBackendInterface>); + ~InputTracer() = default; + InputTracer(const InputTracer&) = delete; + InputTracer& operator=(const InputTracer&) = delete; + + std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override; + void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override; + void eventProcessingComplete(const EventTrackerInterface&) override; + void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override; + +private: + std::unique_ptr<InputTracingBackendInterface> mBackend; + + // The state of a tracked event. + struct EventState { + const TracedEvent event; + // TODO(b/210460522): Add additional args for tracking event sensitivity and + // dispatch target UIDs. + }; + + // Get the event state associated with a tracking cookie. + std::optional<EventState>& getState(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; + + private: + InputTracer& mTracer; + // This event tracker cookie will only hold the state as long as it has not been written + // to the trace. The state is released when the event is written to the trace. + mutable std::optional<EventState> mState; + + friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&); + }; +}; + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h new file mode 100644 index 0000000000..c6cd7de4b6 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h @@ -0,0 +1,87 @@ +/* + * 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 "../Entry.h" +#include "../InputTarget.h" +#include "EventTrackerInterface.h" + +namespace android::inputdispatcher::trace { + +/** + * InputTracerInterface is the tracing interface for InputDispatcher. + * + * The tracer is responsible for tracing information about input events and where they are + * dispatched. The trace is logged to the backend using the InputTracingBackendInterface. + * + * A normal traced event should have the following lifecycle: + * - The EventTracker is obtained from traceInboundEvent(), after which point the event + * should not change. + * - While the event is being processed, dispatchToTargetHint() is called for each target that + * the event will be eventually sent to. + * - Once all targets have been determined, eventProcessingComplete() is called, at which point + * the tracer will have enough information to commit the event to the trace. + * - For each event that is dispatched to the client, traceEventDispatch() is called, and the + * tracer will record that the event was sent to the client. + */ +class InputTracerInterface { +public: + InputTracerInterface() = default; + virtual ~InputTracerInterface() = default; + InputTracerInterface(const InputTracerInterface&) = delete; + InputTracerInterface& operator=(const InputTracerInterface&) = delete; + + /** + * Trace an input event that is being processed by InputDispatcher. The event must not be + * modified after it is traced to keep the traced event consistent with the event that is + * eventually dispatched. An EventTracker is returned for each traced event that should be used + * to track the event's lifecycle inside InputDispatcher. + */ + virtual std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) = 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. + * This may be called 0 or more times for each tracked event before event processing is + * completed. + */ + virtual void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) = 0; + + /** + * Notify the tracer that the event processing is complete. This may be called at most once + * for each traced event. If a tracked event is dropped before it can be processed, it is + * possible that this is never called before the EventTracker is destroyed. + * + * This is used to commit the event to the trace in a timely manner, rather than always + * waiting for the event to go out of scope (and thus for the EventTracker to be destroyed) + * before committing. The point at which the event is destroyed can depend on several factors + * outside of our control, such as how long apps take to respond, so we don't want to depend on + * that. + */ + virtual void eventProcessingComplete(const EventTrackerInterface&) = 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. + */ + virtual void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) = 0; +}; + +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h new file mode 100644 index 0000000000..b0eadfe51f --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h @@ -0,0 +1,105 @@ +/* + * 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 <gui/PidUid.h> +#include <input/Input.h> +#include <ui/Transform.h> + +#include <array> +#include <variant> +#include <vector> + +namespace android::inputdispatcher::trace { + +/** + * A representation of an Android KeyEvent used by the tracing backend. + */ +struct TracedKeyEvent { + int32_t id; + nsecs_t eventTime; + uint32_t policyFlags; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + nsecs_t downTime; + int32_t flags; + int32_t repeatCount; +}; + +/** + * A representation of an Android MotionEvent used by the tracing backend. + */ +struct TracedMotionEvent { + int32_t id; + nsecs_t eventTime; + uint32_t policyFlags; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t actionButton; + int32_t flags; + int32_t metaState; + int32_t buttonState; + MotionClassification classification; + int32_t edgeFlags; + float xPrecision; + float yPrecision; + float xCursorPosition; + float yCursorPosition; + nsecs_t downTime; + std::vector<PointerProperties> pointerProperties; + std::vector<PointerCoords> pointerCoords; +}; + +/** A representation of a traced input event. */ +using TracedEvent = std::variant<TracedKeyEvent, TracedMotionEvent>; + +/** + * An interface for the tracing backend, used for setting a custom backend for testing. + */ +class InputTracingBackendInterface { +public: + virtual ~InputTracingBackendInterface() = default; + + /** Trace a KeyEvent. */ + virtual void traceKeyEvent(const TracedKeyEvent&) = 0; + + /** Trace a MotionEvent. */ + virtual void traceMotionEvent(const TracedMotionEvent&) = 0; + + /** Trace an event being sent to a window. */ + struct WindowDispatchArgs { + TracedEvent eventEntry; + nsecs_t deliveryTime; + int32_t resolvedFlags; + gui::Uid targetUid; + int64_t vsyncId; + int32_t windowId; + ui::Transform transform; + ui::Transform rawTransform; + std::array<uint8_t, 32> hmac; + }; + virtual void traceWindowDispatch(const WindowDispatchArgs&) = 0; +}; + +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp new file mode 100644 index 0000000000..46ad9e16d9 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp @@ -0,0 +1,94 @@ +/* + * 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 LOG_TAG "InputTracer" + +#include "InputTracingPerfettoBackend.h" + +#include "AndroidInputEventProtoConverter.h" + +#include <android-base/logging.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> + +namespace android::inputdispatcher::trace::impl { + +namespace { + +constexpr auto INPUT_EVENT_TRACE_DATA_SOURCE_NAME = "android.input.inputevent"; + +} // namespace + +// --- PerfettoBackend::InputEventDataSource --- + +void PerfettoBackend::InputEventDataSource::OnStart(const perfetto::DataSourceBase::StartArgs&) { + LOG(INFO) << "Starting perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; +} + +void PerfettoBackend::InputEventDataSource::OnStop(const perfetto::DataSourceBase::StopArgs&) { + LOG(INFO) << "Stopping perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { ctx.Flush(); }); +} + +// --- PerfettoBackend --- + +std::once_flag PerfettoBackend::sDataSourceRegistrationFlag{}; + +PerfettoBackend::PerfettoBackend() { + // Use a once-flag to ensure that the data source is only registered once per boot, since + // we never unregister the InputEventDataSource. + std::call_once(sDataSourceRegistrationFlag, []() { + perfetto::TracingInitArgs args; + args.backends = perfetto::kSystemBackend; + perfetto::Tracing::Initialize(args); + + // Register our custom data source for input event tracing. + perfetto::DataSourceDescriptor dsd; + dsd.set_name(INPUT_EVENT_TRACE_DATA_SOURCE_NAME); + InputEventDataSource::Register(dsd); + LOG(INFO) << "InputTracer initialized for data source: " + << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; + }); +} + +void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEvent = tracePacket->set_android_input_event(); + auto* dispatchMotion = inputEvent->set_dispatcher_motion_event(); + AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion); + }); +} + +void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEvent = tracePacket->set_android_input_event(); + auto* dispatchKey = inputEvent->set_dispatcher_key_event(); + AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey); + }); +} + +void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEventProto = tracePacket->set_android_input_event(); + auto* dispatchEventProto = inputEventProto->set_dispatcher_window_dispatch_event(); + AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, + *dispatchEventProto); + }); +} + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h new file mode 100644 index 0000000000..fefcfb3ae0 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h @@ -0,0 +1,66 @@ +/* + * 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 "InputTracingBackendInterface.h" + +#include <perfetto/tracing.h> +#include <mutex> + +namespace android::inputdispatcher::trace::impl { + +/** + * The tracing backend that writes events into ongoing Perfetto traces. + * + * Example shell command to take an input trace from Perfetto: + * + * adb shell perfetto \ + * -c - --txt \ + * -o /data/misc/perfetto-traces/trace.input-trace \ + * <<END + * buffers: { + * size_kb: 5000 + * fill_policy: RING_BUFFER + * } + * data_sources: { + * config { + * name: "android.input.inputevent" + * } + * } + * END + */ +class PerfettoBackend : public InputTracingBackendInterface { +public: + PerfettoBackend(); + ~PerfettoBackend() override = default; + + void traceKeyEvent(const TracedKeyEvent&) override; + void traceMotionEvent(const TracedMotionEvent&) override; + void traceWindowDispatch(const WindowDispatchArgs&) override; + + class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> { + public: + void OnSetup(const SetupArgs&) override {} + void OnStart(const StartArgs&) override; + void OnStop(const StopArgs&) override; + }; + +private: + static std::once_flag sDataSourceRegistrationFlag; +}; + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp new file mode 100644 index 0000000000..25bc2276c9 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp @@ -0,0 +1,110 @@ +/* + * 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 LOG_TAG "InputTracer" + +#include "ThreadedBackend.h" + +#include "InputTracingPerfettoBackend.h" + +#include <android-base/logging.h> + +namespace android::inputdispatcher::trace::impl { + +namespace { + +// Helper to std::visit with lambdas. +template <typename... V> +struct Visitor : V... { + using V::operator()...; +}; + +} // namespace + +// --- ThreadedBackend --- + +template <typename Backend> +ThreadedBackend<Backend>::ThreadedBackend(Backend&& innerBackend) + : mTracerThread( + "InputTracer", [this]() { threadLoop(); }, + [this]() { mThreadWakeCondition.notify_all(); }), + mBackend(std::move(innerBackend)) {} + +template <typename Backend> +ThreadedBackend<Backend>::~ThreadedBackend() { + { + std::scoped_lock lock(mLock); + mThreadExit = true; + } + mThreadWakeCondition.notify_all(); +} + +template <typename Backend> +void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event) { + std::scoped_lock lock(mLock); + mQueue.emplace_back(event); + mThreadWakeCondition.notify_all(); +} + +template <typename Backend> +void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event) { + std::scoped_lock lock(mLock); + mQueue.emplace_back(event); + mThreadWakeCondition.notify_all(); +} + +template <typename Backend> +void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) { + std::scoped_lock lock(mLock); + mQueue.emplace_back(dispatchArgs); + mThreadWakeCondition.notify_all(); +} + +template <typename Backend> +void ThreadedBackend<Backend>::threadLoop() { + std::vector<TraceEntry> entries; + + { // acquire lock + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + // Wait until we need to process more events or exit. + mThreadWakeCondition.wait(lock, + [&]() REQUIRES(mLock) { return mThreadExit || !mQueue.empty(); }); + if (mThreadExit) { + return; + } + + mQueue.swap(entries); + } // release lock + + // Trace the events into the backend without holding the lock to reduce the amount of + // work performed in the critical section. + for (const auto& entry : entries) { + std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend.traceMotionEvent(e); }, + [&](const TracedKeyEvent& e) { mBackend.traceKeyEvent(e); }, + [&](const WindowDispatchArgs& args) { + mBackend.traceWindowDispatch(args); + }}, + entry); + } + entries.clear(); +} + +// Explicit template instantiation for the PerfettoBackend. +template class ThreadedBackend<PerfettoBackend>; + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.h b/services/inputflinger/dispatcher/trace/ThreadedBackend.h new file mode 100644 index 0000000000..5776cf9417 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h @@ -0,0 +1,59 @@ +/* + * 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 "InputThread.h" +#include "InputTracingPerfettoBackend.h" + +#include <android-base/thread_annotations.h> +#include <mutex> +#include <variant> +#include <vector> + +namespace android::inputdispatcher::trace::impl { + +/** + * A wrapper around an InputTracingBackend implementation that writes to the inner tracing backend + * from a single new thread that it creates. The new tracing thread is started when the + * ThreadedBackend is created, and is stopped when it is destroyed. The ThreadedBackend is + * thread-safe. + */ +template <typename Backend> +class ThreadedBackend : public InputTracingBackendInterface { +public: + ThreadedBackend(Backend&& innerBackend); + ~ThreadedBackend() override; + + void traceKeyEvent(const TracedKeyEvent&) override; + void traceMotionEvent(const TracedMotionEvent&) override; + void traceWindowDispatch(const WindowDispatchArgs&) override; + +private: + std::mutex mLock; + InputThread mTracerThread; + bool mThreadExit GUARDED_BY(mLock){false}; + std::condition_variable mThreadWakeCondition; + Backend mBackend; + using TraceEntry = std::variant<TracedKeyEvent, TracedMotionEvent, WindowDispatchArgs>; + std::vector<TraceEntry> mQueue GUARDED_BY(mLock); + + using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs; + + void threadLoop(); +}; + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/include/InputFilterPolicyInterface.h b/services/inputflinger/include/InputFilterPolicyInterface.h new file mode 100644 index 0000000000..4d39b977d5 --- /dev/null +++ b/services/inputflinger/include/InputFilterPolicyInterface.h @@ -0,0 +1,47 @@ +/* + * 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 + +namespace android { + +/** + * The InputFilter policy interface. + * + * This is the interface that InputFilter uses to talk to Input Manager and other system components. + */ +class InputFilterPolicyInterface { +public: + virtual ~InputFilterPolicyInterface() = default; + + /** + * A callback to notify about sticky modifier state changes when Sticky keys feature is enabled. + * + * modifierState: Current sticky modifier state which will be sent with all subsequent + * KeyEvents. This only includes modifiers that can be 'Sticky' which includes: Meta, Ctrl, + * Shift, Alt and AltGr. + * + * lockedModifierState: Current locked modifier state representing modifiers that don't get + * cleared after non-modifier key press. This only includes modifiers that can be 'Sticky' which + * includes: Meta, Ctrl, Shift, Alt and AltGr. + * + * For more information {@see sticky_keys_filter.rs} + */ + virtual void notifyStickyModifierStateChanged(uint32_t modifierState, + uint32_t lockedModifierState) = 0; +}; + +} // namespace android diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index efc8b260f3..79c8a4ba76 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -126,7 +126,20 @@ struct InputReaderConfiguration { // The suggested display ID to show the cursor. int32_t defaultPointerDisplayId; + // The mouse pointer speed, as a number from -7 (slowest) to 7 (fastest). + // + // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled. + int32_t mousePointerSpeed; + + // Displays on which an acceleration curve shouldn't be applied for pointer movements from mice. + // + // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled. + std::set<int32_t> displaysWithMousePointerAccelerationDisabled; + // Velocity control parameters for mouse pointer movements. + // + // If the enable_new_mouse_pointer_ballistics flag is enabled, these are ignored and the values + // of mousePointerSpeed and mousePointerAccelerationEnabled used instead. VelocityControlParameters pointerVelocityControlParameters; // Velocity control parameters for mouse wheel movements. @@ -213,6 +226,9 @@ struct InputReaderConfiguration { // True to enable tap-to-click on touchpads. bool touchpadTapToClickEnabled; + // True to enable tap dragging on touchpads. + bool touchpadTapDraggingEnabled; + // True to enable a zone on the right-hand side of touchpads where clicks will be turned into // context (a.k.a. "right") clicks. bool touchpadRightClickZoneEnabled; @@ -229,6 +245,8 @@ struct InputReaderConfiguration { InputReaderConfiguration() : virtualKeyQuietTime(0), + mousePointerSpeed(0), + displaysWithMousePointerAccelerationDisabled(), pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, static_cast<float>( android::os::IInputConstants:: @@ -251,6 +269,7 @@ struct InputReaderConfiguration { touchpadPointerSpeed(0), touchpadNaturalScrollingEnabled(true), touchpadTapToClickEnabled(true), + touchpadTapDraggingEnabled(false), touchpadRightClickZoneEnabled(false), stylusButtonMotionEventsEnabled(true), stylusPointerIconEnabled(false) {} diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index f954370928..ba586d7a18 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // 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" diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index f7bbc511f9..3ca691efba 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -810,14 +810,20 @@ void EventHub::Device::trackInputEvent(const struct input_event& event) { case EV_SYN: { switch (event.code) { case SYN_REPORT: - currentFrameDropped = false; + if (currentFrameDropped) { + // To recover after a SYN_DROPPED, we need to query the state of the device + // to synchronize our device state with the kernel's to account for the + // dropped events on receiving the next SYN_REPORT. + // Note we don't drop the SYN_REPORT at this point but it is used by the + // InputDevice to reset and repopulate mapper state + readDeviceState(); + currentFrameDropped = false; + } break; case SYN_DROPPED: // When we receive SYN_DROPPED, all events in the current frame should be - // dropped. We query the state of the device to synchronize our device state - // with the kernel's to account for the dropped events. + // dropped up to and including next SYN_REPORT currentFrameDropped = true; - readDeviceState(); break; default: break; @@ -1141,6 +1147,22 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* return OK; } +base::Result<std::vector<int32_t>> EventHub::getMtSlotValues(int32_t deviceId, int32_t axis, + size_t slotCount) const { + std::scoped_lock _l(mLock); + const Device* device = getDeviceLocked(deviceId); + if (device == nullptr || !device->hasValidFd() || !device->absBitmask.test(axis)) { + return base::ResultError("device problem or axis not supported", NAME_NOT_FOUND); + } + std::vector<int32_t> outValues(slotCount + 1); + outValues[0] = axis; + const size_t bufferSize = outValues.size() * sizeof(int32_t); + if (ioctl(device->fd, EVIOCGMTSLOTS(bufferSize), outValues.data()) != OK) { + return base::ErrnoError(); + } + return std::move(outValues); +} + bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) const { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index fb32f96482..2baf576903 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -251,6 +251,7 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, mAssociatedDeviceType = getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location); mIsWaking = mConfiguration.getBool("device.wake").value_or(false); + mShouldSmoothScroll = mConfiguration.getBool("device.viewBehavior_smoothScroll"); } if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) { @@ -264,6 +265,8 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, } if (!changes.any() || changes.test(Change::DISPLAY_INFO)) { + const auto oldAssociatedDisplayId = getAssociatedDisplayId(); + // In most situations, no port or name will be specified. mAssociatedDisplayPort = std::nullopt; mAssociatedDisplayUniqueId = std::nullopt; @@ -305,6 +308,10 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, getName().c_str(), mAssociatedDisplayUniqueId->c_str()); } } + + if (getAssociatedDisplayId() != oldAssociatedDisplayId) { + bumpGeneration(); + } } for_each_mapper([this, when, &readerConfig, changes, &out](InputMapper& mapper) { @@ -350,6 +357,7 @@ std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t cou if (mDropUntilNextSync) { if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + out += reset(rawEvent->when); mDropUntilNextSync = false; ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun."); } else { @@ -359,7 +367,6 @@ std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t cou } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); mDropUntilNextSync = true; - out += reset(rawEvent->when); } else { for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) { out += mapper.process(rawEvent); @@ -401,7 +408,8 @@ std::list<NotifyArgs> InputDevice::updateExternalStylusState(const StylusState& InputDeviceInfo InputDevice::getDeviceInfo() { InputDeviceInfo outDeviceInfo; outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, - mHasMic, getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE)); + mHasMic, getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE), + {mShouldSmoothScroll}); for_each_mapper( [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); }); diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 0582649d38..9608210ca0 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -164,19 +164,6 @@ void InputReader::loopOnce() { std::swap(notifyArgs, mPendingArgs); } // release lock - // Send out a message that the describes the changed input devices. - if (inputDevicesChanged) { - mPolicy->notifyInputDevicesChanged(inputDevices); - } - - // Notify the policy of the start of every new stylus gesture outside the lock. - for (const auto& args : notifyArgs) { - const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args); - if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) { - mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime); - } - } - // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked @@ -187,6 +174,21 @@ void InputReader::loopOnce() { for (const NotifyArgs& args : notifyArgs) { mNextListener.notify(args); } + + // Notify the policy that input devices have changed. + // This must be done after flushing events down the listener chain to ensure that the rest of + // the listeners are synchronized with the changes before the policy reacts to them. + if (inputDevicesChanged) { + mPolicy->notifyInputDevicesChanged(inputDevices); + } + + // Notify the policy of the start of every new stylus gesture. + for (const auto& args : notifyArgs) { + const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args); + if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) { + mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime); + } + } } std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 0bcab42417..a7e06756d0 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -336,6 +336,10 @@ public: virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0; virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const = 0; + /* Query Multi-Touch slot values for an axis. Returns error or an 1 indexed array of size + * (slotCount + 1). The value at the 0 index is set to queried axis. */ + virtual base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis, + size_t slotCount) const = 0; virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0; /* @@ -552,6 +556,8 @@ public: int32_t locationKeyCode) const override final; status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override final; + base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis, + size_t slotCount) const override final; bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) const override final; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 31dcb2e7a8..0719b0ce3c 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -199,6 +199,7 @@ private: std::optional<DisplayViewport> mAssociatedViewport; bool mHasMic; bool mDropUntilNextSync; + std::optional<bool> mShouldSmoothScroll; typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); @@ -280,7 +281,7 @@ private: class InputDeviceContext { public: InputDeviceContext(InputDevice& device, int32_t eventHubId); - ~InputDeviceContext(); + virtual ~InputDeviceContext(); inline InputReaderContext* getContext() { return mContext; } inline int32_t getId() { return mDeviceId; } @@ -372,6 +373,10 @@ public: inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const { return mEventHub->getAbsoluteAxisValue(mId, code, outValue); } + inline base::Result<std::vector<int32_t>> getMtSlotValues(int32_t axis, + size_t slotCount) const { + return mEventHub->getMtSlotValues(mId, axis, slotCount); + } inline bool markSupportedKeyCodes(const std::vector<int32_t>& keyCodes, uint8_t* outFlags) const { return mEventHub->markSupportedKeyCodes(mId, keyCodes, outFlags); @@ -450,7 +455,7 @@ public: inline std::optional<std::string> getDeviceTypeAssociation() const { return mDevice.getDeviceTypeAssociation(); } - inline std::optional<DisplayViewport> getAssociatedViewport() const { + virtual std::optional<DisplayViewport> getAssociatedViewport() const { return mDevice.getAssociatedViewport(); } [[nodiscard]] inline std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) { diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 58e35a6aba..06f10e5445 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -20,9 +20,12 @@ #include "CursorInputMapper.h" -#include <com_android_input_flags.h> #include <optional> +#include <com_android_input_flags.h> +#include <ftl/enum.h> +#include <input/AccelerationCurve.h> + #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" #include "PointerControllerInterface.h" @@ -37,6 +40,8 @@ namespace android { // The default velocity control parameters that has no effect. static const VelocityControlParameters FLAT_VELOCITY_CONTROL_PARAMS{}; +static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer(); + // --- CursorMotionAccumulator --- CursorMotionAccumulator::CursorMotionAccumulator() { @@ -73,9 +78,15 @@ void CursorMotionAccumulator::finishSync() { CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig) + : CursorInputMapper(deviceContext, readerConfig, ENABLE_POINTER_CHOREOGRAPHER) {} + +CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext, + const InputReaderConfiguration& readerConfig, + bool enablePointerChoreographer) : InputMapper(deviceContext, readerConfig), mLastEventTime(std::numeric_limits<nsecs_t>::min()), - mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {} + mEnablePointerChoreographer(enablePointerChoreographer), + mEnableNewMousePointerBallistics(input_flags::enable_new_mouse_pointer_ballistics()) {} CursorInputMapper::~CursorInputMapper() { if (mPointerController != nullptr) { @@ -133,7 +144,7 @@ void CursorInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str()); - dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); + dump += StringPrintf(INDENT3 "Orientation: %s\n", ftl::enum_string(mOrientation).c_str()); dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); @@ -156,15 +167,16 @@ std::list<NotifyArgs> CursorInputMapper::reconfigure(nsecs_t when, out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId())); } - if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || - configurePointerCapture) { - configureOnChangePointerSpeed(readerConfig); - } - if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) { configureOnChangeDisplayInfo(readerConfig); } + + // Pointer speed settings depend on display settings. + if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || + changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) { + configureOnChangePointerSpeed(readerConfig); + } return out; } @@ -204,7 +216,8 @@ std::list<NotifyArgs> CursorInputMapper::reset(nsecs_t when) { mDownTime = 0; mLastEventTime = std::numeric_limits<nsecs_t>::min(); - mPointerVelocityControl.reset(); + mOldPointerVelocityControl.reset(); + mNewPointerVelocityControl.reset(); mWheelXVelocityControl.reset(); mWheelYVelocityControl.reset(); @@ -282,7 +295,11 @@ std::list<NotifyArgs> CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { mWheelYVelocityControl.move(when, nullptr, &vscroll); mWheelXVelocityControl.move(when, &hscroll, nullptr); - mPointerVelocityControl.move(when, &deltaX, &deltaY); + if (mEnableNewMousePointerBallistics) { + mNewPointerVelocityControl.move(when, &deltaX, &deltaY); + } else { + mOldPointerVelocityControl.move(when, &deltaX, &deltaY); + } float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; @@ -492,11 +509,27 @@ void CursorInputMapper::configureOnPointerCapture(const InputReaderConfiguration void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfiguration& config) { if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) { // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled. - mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); + if (mEnableNewMousePointerBallistics) { + mNewPointerVelocityControl.setAccelerationEnabled(false); + } else { + mOldPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); + } mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); } else { - mPointerVelocityControl.setParameters(config.pointerVelocityControlParameters); + if (mEnableNewMousePointerBallistics) { + mNewPointerVelocityControl.setAccelerationEnabled( + config.displaysWithMousePointerAccelerationDisabled.count( + mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0); + mNewPointerVelocityControl.setCurve( + createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)); + } else { + mOldPointerVelocityControl.setParameters( + (config.displaysWithMousePointerAccelerationDisabled.count( + mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0) + ? config.pointerVelocityControlParameters + : FLAT_VELOCITY_CONTROL_PARAMS); + } mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters); mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters); } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 308adaa463..ca541d9924 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -26,7 +26,6 @@ namespace android { -class VelocityControl; class PointerControllerInterface; class CursorButtonAccumulator; @@ -111,9 +110,10 @@ private: // Velocity controls for mouse pointer and wheel movements. // The controls for X and Y wheel movements are separate to keep them decoupled. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; + SimpleVelocityControl mOldPointerVelocityControl; + CurvedVelocityControl mNewPointerVelocityControl; + SimpleVelocityControl mWheelXVelocityControl; + SimpleVelocityControl mWheelYVelocityControl; // The display that events generated by this mapper should target. This can be set to // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e. @@ -129,9 +129,14 @@ private: nsecs_t mLastEventTime; const bool mEnablePointerChoreographer; + const bool mEnableNewMousePointerBallistics; explicit CursorInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig); + // Constructor for testing. + explicit CursorInputMapper(InputDeviceContext& deviceContext, + const InputReaderConfiguration& readerConfig, + bool enablePointerChoreographer); void dumpParameters(std::string& dump); void configureBasicParams(); void configureOnPointerCapture(const InputReaderConfiguration& config); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index f068cc8aa4..738517b67e 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -20,6 +20,7 @@ #include "KeyboardInputMapper.h" +#include <ftl/enum.h> #include <ui/Rotation.h> namespace android { @@ -61,6 +62,36 @@ static bool isSupportedScanCode(int32_t scanCode) { scanCode >= BTN_WHEEL; } +static bool isMediaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MUTE: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MEDIA_SKIP_FORWARD: + case AKEYCODE_MEDIA_SKIP_BACKWARD: + case AKEYCODE_MEDIA_STEP_FORWARD: + case AKEYCODE_MEDIA_STEP_BACKWARD: + case AKEYCODE_MEDIA_AUDIO_TRACK: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_TV_AUDIO_DESCRIPTION: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: + return true; + default: + return false; + } +} + // --- KeyboardInputMapper --- KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, @@ -113,7 +144,7 @@ void KeyboardInputMapper::dump(std::string& dump) { dump += INDENT2 "Keyboard Input Mapper:\n"; dumpParameters(dump); dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); + dump += StringPrintf(INDENT3 "Orientation: %s\n", ftl::enum_string(getOrientation()).c_str()); dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); dump += INDENT3 "KeyboardLayoutInfo: "; @@ -301,7 +332,8 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read // For internal keyboards and devices for which the default wake behavior is explicitly // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each // wake key individually. - if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault) { + if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault && + !(mKeyboardType != AINPUT_KEYBOARD_TYPE_ALPHABETIC && isMediaKey(keyCode))) { policyFlags |= POLICY_FLAG_WAKE; } diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 82f92767d9..bca9d9183b 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -35,10 +35,8 @@ MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext, MultiTouchInputMapper::~MultiTouchInputMapper() {} std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) { - // TODO(b/291626046): Sync the MT state with the kernel using EVIOCGMTSLOTS. - mMultiTouchMotionAccumulator.reset(getDeviceContext()); mPointerIdBits.clear(); - + mMultiTouchMotionAccumulator.reset(mDeviceContext); return TouchInputMapper::reset(when); } diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp index f79219f151..9ec02a6f86 100644 --- a/services/inputflinger/reader/mapper/SlopController.cpp +++ b/services/inputflinger/reader/mapper/SlopController.cpp @@ -54,11 +54,13 @@ float SlopController::consumeEvent(nsecs_t eventTimeNanos, float value) { mCumulativeValue += value; if (abs(mCumulativeValue) >= mSlopThreshold) { + ALOGD("SlopController: did not drop event with value .%3f", value); mHasSlopBeenMet = true; // Return the amount of value that exceeds the slop. return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold); } + ALOGD("SlopController: dropping event with value .%3f", value); return 0; } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index bd9371d263..4b39e4099c 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -708,9 +708,9 @@ private: } mPointerSimple; // The pointer and scroll velocity controls. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; + SimpleVelocityControl mPointerVelocityControl; + SimpleVelocityControl mWheelXVelocityControl; + SimpleVelocityControl mWheelYVelocityControl; std::optional<DisplayViewport> findViewport(); diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 34ca0b3767..eacc66eeab 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -21,12 +21,15 @@ #include <iterator> #include <limits> #include <map> +#include <mutex> #include <optional> #include <android-base/stringprintf.h> +#include <android-base/thread_annotations.h> #include <android/input.h> #include <com_android_input_flags.h> #include <ftl/enum.h> +#include <input/AccelerationCurve.h> #include <input/PrintTools.h> #include <linux/input-event-codes.h> #include <log/log_main.h> @@ -44,6 +47,8 @@ namespace android { namespace { +static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer(); + /** * Log details of each gesture output by the gestures library. * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires @@ -53,27 +58,10 @@ const bool DEBUG_TOUCHPAD_GESTURES = __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures", ANDROID_LOG_INFO); -// Describes a segment of the acceleration curve. -struct CurveSegment { - // The maximum pointer speed which this segment should apply. The last segment in a curve should - // always set this to infinity. - double maxPointerSpeedMmPerS; - double slope; - double intercept; -}; - -const std::vector<CurveSegment> segments = { - {32.002, 3.19, 0}, - {52.83, 4.79, -51.254}, - {119.124, 7.28, -182.737}, - {std::numeric_limits<double>::infinity(), 15.04, -1107.556}, -}; - -const std::vector<double> sensitivityFactors = {1, 2, 4, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 16, 18, 20}; - std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity, size_t propertySize) { + std::vector<AccelerationCurveSegment> segments = + createAccelerationCurveForPointerSensitivity(sensitivity); LOG_ALWAYS_FATAL_IF(propertySize < 4 * segments.size()); std::vector<double> output(propertySize, 0); @@ -83,31 +71,23 @@ std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity, // // (a, b, and c are also called sqr_, mul_, and int_ in the Gestures library code.) // - // We are trying to implement the following function, where slope and intercept are the - // parameters specified in the `segments` array above: - // gain(input_speed_mm) = - // 0.64 * (sensitivityFactor / 10) * (slope + intercept / input_speed_mm) + // createAccelerationCurveForPointerSensitivity gives us parameters for a function of the form: + // gain(input_speed_mm) = baseGain + reciprocal / input_speed_mm // Where "gain" is a multiplier applied to the input speed to produce the output speed: // output_speed(input_speed_mm) = input_speed_mm * gain(input_speed_mm) // // To put our function in the library's form, we substitute it into the function above: - // output_speed(input_speed_mm) = - // input_speed_mm * (0.64 * (sensitivityFactor / 10) * - // (slope + 25.4 * intercept / input_speed_mm)) - // then expand the brackets so that input_speed_mm cancels out for the intercept term: - // gain(input_speed_mm) = - // 0.64 * (sensitivityFactor / 10) * slope * input_speed_mm + - // 0.64 * (sensitivityFactor / 10) * intercept + // output_speed(input_speed_mm) = input_speed_mm * (baseGain + reciprocal / input_speed_mm) + // then expand the brackets so that input_speed_mm cancels out for the reciprocal term: + // gain(input_speed_mm) = baseGain * input_speed_mm + reciprocal // // This gives us the following parameters for the Gestures library function form: // a = 0 - // b = 0.64 * (sensitivityFactor / 10) * slope - // c = 0.64 * (sensitivityFactor / 10) * intercept - - double commonFactor = 0.64 * sensitivityFactors[sensitivity + 7] / 10; + // b = baseGain + // c = reciprocal size_t i = 0; - for (CurveSegment seg : segments) { + for (AccelerationCurveSegment seg : segments) { // The library's curve format consists of four doubles per segment: // * maximum pointer speed for the segment (mm/s) // * multiplier for the x² term (a.k.a. "a" or "sqr") @@ -116,8 +96,8 @@ std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity, // (see struct CurveSegment in the library's AccelFilterInterpreter) output[i + 0] = seg.maxPointerSpeedMmPerS; output[i + 1] = 0; - output[i + 2] = commonFactor * seg.slope; - output[i + 3] = commonFactor * seg.intercept; + output[i + 2] = seg.baseGain; + output[i + 3] = seg.reciprocal; i += 4; } @@ -156,13 +136,20 @@ public: return sAccumulator; } - void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].fingers++; } + void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) { + std::scoped_lock lock(mLock); + mCounters[id].fingers++; + } - void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].palms++; } + void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) { + std::scoped_lock lock(mLock); + mCounters[id].palms++; + } // Checks whether a Gesture struct is for the end of a gesture that we log metrics for, and // records it if so. void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) { + std::scoped_lock lock(mLock); switch (gesture.type) { case kGestureTypeFling: if (gesture.details.fling.fling_state == GESTURES_FLING_START) { @@ -200,15 +187,20 @@ private: void* cookie) { LOG_ALWAYS_FATAL_IF(atomTag != android::util::TOUCHPAD_USAGE); MetricsAccumulator& accumulator = MetricsAccumulator::getInstance(); - accumulator.produceAtoms(outEventList); - accumulator.resetCounters(); + accumulator.produceAtomsAndReset(*outEventList); return AStatsManager_PULL_SUCCESS; } - void produceAtoms(AStatsEventList* outEventList) const { + void produceAtomsAndReset(AStatsEventList& outEventList) { + std::scoped_lock lock(mLock); + produceAtomsLocked(outEventList); + resetCountersLocked(); + } + + void produceAtomsLocked(AStatsEventList& outEventList) const REQUIRES(mLock) { for (auto& [id, counters] : mCounters) { auto [busId, vendorId, productId, versionId] = id; - addAStatsEvent(outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId, + addAStatsEvent(&outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId, versionId, linuxBusToInputDeviceBusEnum(busId, /*isUsi=*/false), counters.fingers, counters.palms, counters.twoFingerSwipeGestures, counters.threeFingerSwipeGestures, counters.fourFingerSwipeGestures, @@ -216,7 +208,7 @@ private: } } - void resetCounters() { mCounters.clear(); } + void resetCountersLocked() REQUIRES(mLock) { mCounters.clear(); } // Stores the counters for a specific touchpad model. Fields have the same meanings as those of // the TouchpadUsage atom; see that definition for detailed documentation. @@ -232,13 +224,21 @@ private: // Metrics are aggregated by device model and version, so if two devices of the same model and // version are connected at once, they will have the same counters. - std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters; + std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters GUARDED_BY(mLock); + + // Metrics are pulled by a binder thread, so we need to guard them with a mutex. + mutable std::mutex mLock; }; } // namespace TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig) + : TouchpadInputMapper(deviceContext, readerConfig, ENABLE_POINTER_CHOREOGRAPHER) {} + +TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext, + const InputReaderConfiguration& readerConfig, + bool enablePointerChoreographer) : InputMapper(deviceContext, readerConfig), mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter), mPointerController(getContext()->getPointerController(getDeviceId())), @@ -247,7 +247,7 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext, mGestureConverter(*getContext(), deviceContext, getDeviceId()), mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()), mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())), - mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) { + mEnablePointerChoreographer(enablePointerChoreographer) { RawAbsoluteAxisInfo slotAxisInfo; deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo); if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) { @@ -382,6 +382,8 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, : FloatRect{0, 0, 0, 0}; } mGestureConverter.setBoundsInLogicalDisplay(*boundsInLogicalDisplay); + + bumpGeneration(); } if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) { mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve") @@ -402,6 +404,8 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, .setBoolValues({config.touchpadNaturalScrollingEnabled}); mPropertyProvider.getProperty("Tap Enable") .setBoolValues({config.touchpadTapToClickEnabled}); + mPropertyProvider.getProperty("Tap Drag Enable") + .setBoolValues({config.touchpadTapDraggingEnabled}); mPropertyProvider.getProperty("Button Right Click Zone Enable") .setBoolValues({config.touchpadRightClickZoneEnabled}); } @@ -449,6 +453,9 @@ std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) { if (mPointerCaptured) { return mCapturedEventConverter.process(*rawEvent); } + if (mMotionAccumulator.getActiveSlotsCount() == 0) { + mGestureStartTime = rawEvent->when; + } std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent); if (state) { updatePalmDetectionMetrics(); @@ -514,7 +521,7 @@ std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t if (mDisplayId) { MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance(); for (Gesture& gesture : mGesturesToProcess) { - out += mGestureConverter.handleGesture(when, readTime, gesture); + out += mGestureConverter.handleGesture(when, readTime, mGestureStartTime, gesture); metricsAccumulator.processGesture(mMetricsId, gesture); } } diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h index ece0eca0e7..9f272cf846 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -72,6 +72,10 @@ private: void resetGestureInterpreter(nsecs_t when); explicit TouchpadInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig); + // Constructor for testing. + explicit TouchpadInputMapper(InputDeviceContext& deviceContext, + const InputReaderConfiguration& readerConfig, + bool enablePointerChoreographer); void updatePalmDetectionMetrics(); [[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime, SelfContainedHardwareState schs); @@ -113,6 +117,8 @@ private: // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e. // std::nullopt), all events will be ignored. std::optional<int32_t> mDisplayId; + + nsecs_t mGestureStartTime{0}; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp index d06514acab..b3f170075c 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -30,30 +30,12 @@ void MultiTouchMotionAccumulator::configure(const InputDeviceContext& deviceCont size_t slotCount, bool usingSlotsProtocol) { mUsingSlotsProtocol = usingSlotsProtocol; mSlots = std::vector<Slot>(slotCount); - reset(deviceContext); + populateCurrentSlot(deviceContext); } void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) { resetSlots(); - - if (!mUsingSlotsProtocol) { - return; - } - - // Query the driver for the current slot index and use it as the initial slot before we - // start reading events from the device. It is possible that the current slot index will - // not be the same as it was when the first event was written into the evdev buffer, which - // means the input mapper could start out of sync with the initial state of the events in - // the evdev buffer. In the extremely unlikely case that this happens, the data from two - // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the - // touch point to "jump", but at least there will be no stuck touches. - int32_t initialSlot; - if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); - status == OK) { - mCurrentSlot = initialSlot; - } else { - ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); - } + syncSlots(deviceContext); } void MultiTouchMotionAccumulator::resetSlots() { @@ -90,54 +72,10 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { if (!mUsingSlotsProtocol) { slot.mInUse = true; } - - switch (rawEvent->code) { - case ABS_MT_POSITION_X: - slot.mAbsMtPositionX = rawEvent->value; - warnIfNotInUse(*rawEvent, slot); - break; - case ABS_MT_POSITION_Y: - slot.mAbsMtPositionY = rawEvent->value; - warnIfNotInUse(*rawEvent, slot); - break; - case ABS_MT_TOUCH_MAJOR: - slot.mAbsMtTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot.mAbsMtTouchMinor = rawEvent->value; - slot.mHaveAbsMtTouchMinor = true; - break; - case ABS_MT_WIDTH_MAJOR: - slot.mAbsMtWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot.mAbsMtWidthMinor = rawEvent->value; - slot.mHaveAbsMtWidthMinor = true; - break; - case ABS_MT_ORIENTATION: - slot.mAbsMtOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - // The slot is no longer in use but it retains its previous contents, - // which may be reused for subsequent touches. - slot.mInUse = false; - } else { - slot.mInUse = true; - slot.mAbsMtTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot.mAbsMtPressure = rawEvent->value; - break; - case ABS_MT_DISTANCE: - slot.mAbsMtDistance = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot.mAbsMtToolType = rawEvent->value; - slot.mHaveAbsMtToolType = true; - break; + if (rawEvent->code == ABS_MT_POSITION_X || rawEvent->code == ABS_MT_POSITION_Y) { + warnIfNotInUse(*rawEvent, slot); } + slot.populateAxisValue(rawEvent->code, rawEvent->value); } } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { // MultiTouch Sync: The driver has returned all data for *one* of the pointers. @@ -145,6 +83,36 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { } } +void MultiTouchMotionAccumulator::syncSlots(const InputDeviceContext& deviceContext) { + if (!mUsingSlotsProtocol) { + return; + } + constexpr std::array<int32_t, 11> axisCodes = {ABS_MT_POSITION_X, ABS_MT_POSITION_Y, + ABS_MT_TOUCH_MAJOR, ABS_MT_TOUCH_MINOR, + ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR, + ABS_MT_ORIENTATION, ABS_MT_TRACKING_ID, + ABS_MT_PRESSURE, ABS_MT_DISTANCE, + ABS_MT_TOOL_TYPE}; + const size_t numSlots = mSlots.size(); + for (int32_t axisCode : axisCodes) { + if (!deviceContext.hasAbsoluteAxis(axisCode)) { + continue; + } + const auto result = deviceContext.getMtSlotValues(axisCode, numSlots); + if (result.ok()) { + const std::vector<int32_t>& mtSlotValues = result.value(); + for (size_t i = 1; i <= numSlots; ++i) { + // The returned slot values are in a 1-indexed vector of size numSlots + 1. + mSlots[i - 1].populateAxisValue(axisCode, mtSlotValues[i]); + } + } else { + ALOGE("Could not retrieve multi-touch slot value for axis=%d error=%s status=%d", + axisCode, result.error().message().c_str(), result.error().code().value()); + } + } + populateCurrentSlot(deviceContext); +} + void MultiTouchMotionAccumulator::finishSync() { if (!mUsingSlotsProtocol) { resetSlots(); @@ -166,6 +134,21 @@ size_t MultiTouchMotionAccumulator::getActiveSlotsCount() const { [](const Slot& slot) { return slot.mInUse; }); } +void MultiTouchMotionAccumulator::populateCurrentSlot( + const android::InputDeviceContext& deviceContext) { + if (!mUsingSlotsProtocol) { + return; + } + int32_t initialSlot; + if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); + status == OK) { + mCurrentSlot = initialSlot; + } else { + ALOGE("Could not retrieve current multi-touch slot index. status=%s", + statusToString(status).c_str()); + } +} + // --- MultiTouchMotionAccumulator::Slot --- ToolType MultiTouchMotionAccumulator::Slot::getToolType() const { @@ -182,4 +165,52 @@ ToolType MultiTouchMotionAccumulator::Slot::getToolType() const { return ToolType::UNKNOWN; } +void MultiTouchMotionAccumulator::Slot::populateAxisValue(int32_t axisCode, int32_t value) { + switch (axisCode) { + case ABS_MT_POSITION_X: + mAbsMtPositionX = value; + break; + case ABS_MT_POSITION_Y: + mAbsMtPositionY = value; + break; + case ABS_MT_TOUCH_MAJOR: + mAbsMtTouchMajor = value; + break; + case ABS_MT_TOUCH_MINOR: + mAbsMtTouchMinor = value; + mHaveAbsMtTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + mAbsMtWidthMajor = value; + break; + case ABS_MT_WIDTH_MINOR: + mAbsMtWidthMinor = value; + mHaveAbsMtWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + mAbsMtOrientation = value; + break; + case ABS_MT_TRACKING_ID: + if (value < 0) { + // The slot is no longer in use but it retains its previous contents, + // which may be reused for subsequent touches. + mInUse = false; + } else { + mInUse = true; + mAbsMtTrackingId = value; + } + break; + case ABS_MT_PRESSURE: + mAbsMtPressure = value; + break; + case ABS_MT_DISTANCE: + mAbsMtDistance = value; + break; + case ABS_MT_TOOL_TYPE: + mAbsMtToolType = value; + mHaveAbsMtToolType = true; + break; + } +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h index 5b55e3d599..a0f21470c4 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -68,12 +68,14 @@ public: int32_t mAbsMtToolType = 0; void clear() { *this = Slot(); } + void populateAxisValue(int32_t axisCode, int32_t value); }; MultiTouchMotionAccumulator(); void configure(const InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); + void reset(const InputDeviceContext& deviceContext); void process(const RawEvent* rawEvent); void finishSync(); @@ -83,15 +85,16 @@ public: LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); return mSlots[index]; } - void reset(const InputDeviceContext& deviceContext); private: - int32_t mCurrentSlot; + int32_t mCurrentSlot{-1}; std::vector<Slot> mSlots; bool mUsingSlotsProtocol; void resetSlots(); + void syncSlots(const InputDeviceContext& deviceContext); void warnIfNotInUse(const RawEvent& event, const Slot& slot); + void populateCurrentSlot(const android::InputDeviceContext& deviceContext); }; } // namespace android diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 955210479f..764bb56141 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -35,8 +35,14 @@ namespace android { namespace { +// This will disable the tap to click while the user is typing on a physical keyboard const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection(); +// In addition to v1, v2 will also cancel ongoing move gestures while typing and add delay in +// re-enabling the tap to click. +const bool ENABLE_TOUCHPAD_PALM_REJECTION_V2 = + input_flags::enable_v2_touchpad_typing_palm_rejection(); + uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) { switch (gesturesButton) { case GESTURES_BUTTON_LEFT: @@ -61,7 +67,7 @@ GestureConverter::GestureConverter(InputReaderContext& readerContext, : mDeviceId(deviceId), mReaderContext(readerContext), mPointerController(readerContext.getPointerController(deviceId)), - mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) { + mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) { deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo); deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo); } @@ -75,6 +81,8 @@ std::string GestureConverter::dump() const { out << StringPrintf("Button state: 0x%08x\n", mButtonState); out << "Down time: " << mDownTime << "\n"; out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n"; + out << "Is hovering: " << mIsHovering << "\n"; + out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n"; return out.str(); } @@ -82,7 +90,7 @@ std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) { std::list<NotifyArgs> out; switch (mCurrentClassification) { case MotionClassification::TWO_FINGER_SWIPE: - out.push_back(endScroll(when, when)); + out += endScroll(when, when); break; case MotionClassification::MULTI_FINGER_SWIPE: out += handleMultiFingerSwipeLift(when, when); @@ -109,8 +117,6 @@ std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) { void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const { info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0); - // TODO(b/259547750): set this using the raw axis ranges from the touchpad when pointer capture - // is enabled. if (!mBoundsInLogicalDisplay.isEmpty()) { info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, mBoundsInLogicalDisplay.left, mBoundsInLogicalDisplay.right, 0, 0, 0); @@ -131,6 +137,7 @@ void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const { } std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, const Gesture& gesture) { if (!mDisplayId) { // Ignore gestures when there is no target display configured. @@ -139,13 +146,13 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read switch (gesture.type) { case kGestureTypeMove: - return {handleMove(when, readTime, gesture)}; + return handleMove(when, readTime, gestureStartTime, gesture); case kGestureTypeButtonsChange: return handleButtonsChange(when, readTime, gesture); case kGestureTypeScroll: return handleScroll(when, readTime, gesture); case kGestureTypeFling: - return handleFling(when, readTime, gesture); + return handleFling(when, readTime, gestureStartTime, gesture); case kGestureTypeSwipe: return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx, gesture.details.swipe.dy); @@ -162,35 +169,64 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read } } -NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, - const Gesture& gesture) { +std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, + const Gesture& gesture) { float deltaX = gesture.details.move.dx; float deltaY = gesture.details.move.dy; - if (ENABLE_TOUCHPAD_PALM_REJECTION && (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) { - enableTapToClick(); + const auto [oldXCursorPosition, oldYCursorPosition] = mPointerController->getPosition(); + if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) { + bool wasHoverCancelled = mIsHoverCancelled; + // Gesture will be cancelled if it started before the user started typing and + // there is a active IME connection. + mIsHoverCancelled = gestureStartTime <= mReaderContext.getLastKeyDownTimestamp() && + mReaderContext.getPolicy()->isInputMethodConnectionActive(); + + if (!wasHoverCancelled && mIsHoverCancelled) { + // This is the first event of the cancelled gesture, we won't return because we need to + // generate a HOVER_EXIT event + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); + return exitHover(when, readTime, oldXCursorPosition, oldYCursorPosition); + } else if (mIsHoverCancelled) { + return {}; + } } + rotateDelta(mOrientation, &deltaX, &deltaY); - mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); - mPointerController->move(deltaX, deltaY); - mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); + // Update the cursor, and enable tap to click if the gesture is not cancelled + if (!mIsHoverCancelled) { + // handleFling calls hoverMove with zero delta on FLING_TAP_DOWN. Don't enable tap to click + // for this case as subsequent handleButtonsChange may choose to ignore this tap. + if ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) && + (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) { + enableTapToClick(when); + } + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); + mPointerController->move(deltaX, deltaY); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); + } - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); + std::list<NotifyArgs> out; + const bool down = isPointerDown(mButtonState); + if (!down) { + out += enterHover(when, readTime, oldXCursorPosition, oldYCursorPosition); + } + const auto [newXCursorPosition, newYCursorPosition] = mPointerController->getPosition(); PointerCoords coords; coords.clear(); - coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); - coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, newXCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, newYCursorPosition); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - const bool down = isPointerDown(mButtonState); coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE; - return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState, - /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition); + out.push_back(makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState, + /*pointerCount=*/1, &coords, newXCursorPosition, + newYCursorPosition)); + return out; } std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime, @@ -200,8 +236,7 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); PointerCoords coords; coords.clear(); @@ -210,8 +245,15 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); - if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) { - enableTapToClick(); + // V2 palm rejection should override V1 + if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) { + enableTapToClick(when); + if (gesture.details.buttons.is_tap && when <= mWhenToEnableTapToClick) { + // return early to prevent this tap + return out; + } + } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) { + enableTapToClick(when); if (gesture.details.buttons.is_tap) { // return early to prevent this tap return out; @@ -232,16 +274,16 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ newButtonState |= actionButton; pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, newButtonState, - /* pointerCount= */ 1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); + /*pointerCount=*/1, &coords, xCursorPosition, + yCursorPosition)); } } if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) { mDownTime = when; + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + &coords, xCursorPosition, yCursorPosition)); } out.splice(out.end(), pressEvents); @@ -257,20 +299,16 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ newButtonState &= ~actionButton; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, newButtonState, /* pointerCount= */ 1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + &coords, xCursorPosition, yCursorPosition)); } } if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) { coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, - newButtonState, /* pointerCount= */ 1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); - // Send a HOVER_MOVE to tell the application that the mouse is hovering again. - out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_HOVER_MOVE, - /*actionButton=*/0, newButtonState, /*pointerCount=*/1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + newButtonState, /* pointerCount= */ 1, &coords, + xCursorPosition, yCursorPosition)); + mButtonState = newButtonState; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); } mButtonState = newButtonState; return out; @@ -278,8 +316,7 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) { std::list<NotifyArgs> out; - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); PointerCoords coords; coords.clear(); @@ -295,18 +332,18 @@ std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t if (mButtonState & button) { newButtonState &= ~button; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, - button, newButtonState, /*pointerCount=*/1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + button, newButtonState, /*pointerCount=*/1, &coords, + xCursorPosition, yCursorPosition)); } } + mButtonState = 0; if (pointerDown) { coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, - newButtonState, /*pointerCount=*/1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); + mButtonState, /*pointerCount=*/1, &coords, xCursorPosition, + yCursorPosition)); + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); } - mButtonState = 0; return out; } @@ -314,9 +351,10 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT const Gesture& gesture) { std::list<NotifyArgs> out; PointerCoords& coords = mFakeFingerCoords[0]; - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE; coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); @@ -324,8 +362,8 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT mDownTime = when; NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); } @@ -340,14 +378,15 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy); NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); return out; } std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, const Gesture& gesture) { switch (gesture.details.fling.fling_state) { case GESTURES_FLING_START: @@ -356,23 +395,54 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi // ensure consistency between touchscreen and touchpad flings), so we're just using // the "start fling" gestures as a marker for the end of a two-finger scroll // gesture. - return {endScroll(when, readTime)}; + mFlingMayBeInProgress = true; + return endScroll(when, readTime); } break; case GESTURES_FLING_TAP_DOWN: if (mCurrentClassification == MotionClassification::NONE) { - // Use the tap down state of a fling gesture as an indicator that a contact - // has been initiated with the touchpad. We treat this as a move event with zero - // magnitude, which will also result in the pointer icon being updated. - // TODO(b/282023644): Add a signal in libgestures for when a stable contact has been - // initiated with a touchpad. - if (!mReaderContext.isPreventingTouchpadTaps()) { - enableTapToClick(); + if (mEnableFlingStop && mFlingMayBeInProgress) { + // The user has just touched the pad again after ending a two-finger scroll + // motion, which might have started a fling. We want to stop the fling, but + // unfortunately there's currently no API for doing so. Instead, send and + // immediately cancel a fake finger to cause the scrolling to stop and hopefully + // avoid side effects (e.g. activation of UI elements). + // TODO(b/326056750): add an API for fling stops. + mFlingMayBeInProgress = false; + const auto [xCursorPosition, yCursorPosition] = + mPointerController->getPosition(); + PointerCoords coords; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); + + std::list<NotifyArgs> out; + mDownTime = when; + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + // TODO(b/281106755): add a MotionClassification value for fling stops. + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, + /*actionButton=*/0, /*buttonState=*/0, + /*pointerCount=*/1, &coords, xCursorPosition, + yCursorPosition)); + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_CANCEL, + /*actionButton=*/0, /*buttonState=*/0, + /*pointerCount=*/1, &coords, xCursorPosition, + yCursorPosition)); + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); + return out; + } else { + // Use the tap down state of a fling gesture as an indicator that a contact + // has been initiated with the touchpad. We treat this as a move event with zero + // magnitude, which will also result in the pointer icon being updated. + // TODO(b/282023644): Add a signal in libgestures for when a stable contact has + // been initiated with a touchpad. + return handleMove(when, readTime, gestureStartTime, + Gesture(kGestureMove, gesture.start_time, gesture.end_time, + /*dx=*/0.f, + /*dy=*/0.f)); } - return {handleMove(when, readTime, - Gesture(kGestureMove, gesture.start_time, gesture.end_time, - /*dx=*/0.f, - /*dy=*/0.f))}; } break; default: @@ -382,18 +452,20 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi return {}; } -NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); +std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; + const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0); NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; + out.push_back(args); mCurrentClassification = MotionClassification::NONE; - return args; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); + return out; } [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when, @@ -402,14 +474,17 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { float dx, float dy) { std::list<NotifyArgs> out = {}; - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) { // If the user changes the number of fingers mid-way through a swipe (e.g. they start with // three and then put a fourth finger down), the gesture library will treat it as two // separate swipes with an appropriate lift event between them, so we don't have to worry // about the finger count changing mid-swipe. + + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE; + mSwipeFingerCount = fingerCount; constexpr float FAKE_FINGER_SPACING = 100; @@ -428,16 +503,14 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { fingerCount); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); for (size_t i = 1; i < mSwipeFingerCount; i++) { out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_POINTER_DOWN | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), /* actionButton= */ 0, mButtonState, - /* pointerCount= */ i + 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + /* pointerCount= */ i + 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)); } } float rotatedDeltaX = dx, rotatedDeltaY = -dy; @@ -455,8 +528,7 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, mButtonState, /* pointerCount= */ mSwipeFingerCount, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); return out; } @@ -466,8 +538,7 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) { return out; } - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0); @@ -476,23 +547,21 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_ACTION_POINTER_UP | ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), /* actionButton= */ 0, mButtonState, /* pointerCount= */ i, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); } out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT, 0); mCurrentClassification = MotionClassification::NONE; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); mSwipeFingerCount = 0; return out; } [[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime, const Gesture& gesture) { - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); // Pinch gesture phases are reported a little differently from others, in that the same details // struct is used for all phases of the gesture, just with different zoom_state values. When @@ -503,6 +572,10 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START, "First pinch gesture does not have the START zoom state (%d instead).", gesture.details.pinch.zoom_state); + std::list<NotifyArgs> out; + + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::PINCH; mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX; mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0); @@ -515,17 +588,14 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); mDownTime = when; - std::list<NotifyArgs> out; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); return out; } @@ -543,34 +613,66 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { xCursorPosition + mPinchFingerSeparation / 2); mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, - mButtonState, /*pointerCount=*/2, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)}; + mButtonState, /*pointerCount=*/2, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)}; } std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) { std::list<NotifyArgs> out; - const auto [xCursorPosition, yCursorPosition] = - mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_POINTER_UP | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT, /*actionButton=*/0, mButtonState, /*pointerCount=*/2, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); - out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, - mButtonState, /*pointerCount=*/1, mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); - mCurrentClassification = MotionClassification::NONE; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, + mButtonState, /*pointerCount=*/1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0); + mCurrentClassification = MotionClassification::NONE; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); return out; } +std::list<NotifyArgs> GestureConverter::enterHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition) { + if (!mIsHovering) { + mIsHovering = true; + return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_ENTER, xCursorPosition, + yCursorPosition)}; + } else { + return {}; + } +} + +std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition) { + if (mIsHovering) { + mIsHovering = false; + return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_EXIT, xCursorPosition, + yCursorPosition)}; + } else { + return {}; + } +} + +NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action, + float xCursorPosition, float yCursorPosition) { + PointerCoords coords; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); + return makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState, + /*pointerCount=*/1, &coords, xCursorPosition, yCursorPosition); +} + NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, int32_t actionButton, int32_t buttonState, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xCursorPosition, float yCursorPosition) { return {mReaderContext.getNextId(), @@ -588,7 +690,7 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, - pointerProperties, + mFingerProps.data(), pointerCoords, /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, @@ -598,8 +700,11 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime /* videoFrames= */ {}}; } -void GestureConverter::enableTapToClick() { - mReaderContext.setPreventingTouchpadTaps(false); +void GestureConverter::enableTapToClick(nsecs_t when) { + if (mReaderContext.isPreventingTouchpadTaps()) { + mWhenToEnableTapToClick = when + TAP_ENABLE_DELAY_NANOS.count(); + mReaderContext.setPreventingTouchpadTaps(false); + } } } // namespace android diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index 732a4b2ffb..c8f437e0b8 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -34,6 +34,13 @@ namespace android { +using std::chrono_literals::operator""ms; +/** + * This duration is decided based on internal team testing, it may be updated after testing with + * larger groups + */ +constexpr std::chrono::nanoseconds TAP_ENABLE_DELAY_NANOS = 400ms; + // Converts Gesture structs from the gestures library into NotifyArgs and the appropriate // PointerController calls. class GestureConverter { @@ -53,19 +60,22 @@ public: void populateMotionRanges(InputDeviceInfo& info) const; [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, const Gesture& gesture); private: - [[nodiscard]] NotifyMotionArgs handleMove(nsecs_t when, nsecs_t readTime, - const Gesture& gesture); + [[nodiscard]] std::list<NotifyArgs> handleMove(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, + const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime, const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> releaseAllButtons(nsecs_t when, nsecs_t readTime); [[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime, const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> handleFling(nsecs_t when, nsecs_t readTime, + nsecs_t gestureStartTime, const Gesture& gesture); - [[nodiscard]] NotifyMotionArgs endScroll(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> endScroll(nsecs_t when, nsecs_t readTime); [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime, uint32_t fingerCount, float dx, @@ -75,19 +85,27 @@ private: const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> endPinch(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> enterHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition); + [[nodiscard]] std::list<NotifyArgs> exitHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition); + + NotifyMotionArgs makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action, + float xCursorPosition, float yCursorPosition); + NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, int32_t actionButton, int32_t buttonState, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xCursorPosition, - float yCursorPosition); + uint32_t pointerCount, const PointerCoords* pointerCoords, + float xCursorPosition, float yCursorPosition); - void enableTapToClick(); + void enableTapToClick(nsecs_t when); + bool mIsHoverCancelled{false}; + nsecs_t mWhenToEnableTapToClick{0}; const int32_t mDeviceId; InputReaderContext& mReaderContext; std::shared_ptr<PointerControllerInterface> mPointerController; - const bool mEnablePointerChoreographer; + const bool mEnableFlingStop; std::optional<int32_t> mDisplayId; FloatRect mBoundsInLogicalDisplay{}; @@ -99,6 +117,12 @@ private: // button values (AMOTION_EVENT_BUTTON_...). uint32_t mButtonState = 0; nsecs_t mDownTime = 0; + // Whether we are currently in a hover state (i.e. a HOVER_ENTER event has been sent without a + // matching HOVER_EXIT). + bool mIsHovering = false; + // Whether we've received a "fling start" gesture (i.e. the end of a scroll) but no "fling tap + // down" gesture to match it yet. + bool mFlingMayBeInProgress = false; MotionClassification mCurrentClassification = MotionClassification::NONE; // Only used when mCurrentClassification is MULTI_FINGER_SWIPE. diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp index b1e1aee02a..e85a10428d 100644 --- a/services/inputflinger/reporter/Android.bp +++ b/services/inputflinger/reporter/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // 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" diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp index 2803805619..255c7eb679 100644 --- a/services/inputflinger/rust/Android.bp +++ b/services/inputflinger/rust/Android.bp @@ -13,6 +13,10 @@ // limitations under the License. // Generate the C++ code that Rust calls into. +package { + default_team: "trendy_team_input_framework", +} + genrule { name: "inputflinger_rs_bootstrap_bridge_code", tools: ["cxxbridge"], @@ -42,6 +46,7 @@ rust_defaults { "libbinder_rs", "liblog_rust", "liblogger", + "libnix", ], host_supported: true, } diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs index 894b881638..2d5039a1b1 100644 --- a/services/inputflinger/rust/bounce_keys_filter.rs +++ b/services/inputflinger/rust/bounce_keys_filter.rs @@ -118,6 +118,10 @@ impl Filter for BounceKeysFilter { } self.next.notify_devices_changed(device_infos); } + + fn destroy(&mut self) { + self.next.destroy(); + } } #[cfg(test)] diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs index 340ff8e296..a544fa36ae 100644 --- a/services/inputflinger/rust/input_filter.rs +++ b/services/inputflinger/rust/input_filter.rs @@ -22,11 +22,15 @@ use binder::{Interface, Strong}; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, IInputFilter::{IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks}, + IInputThread::{IInputThread, IInputThreadCallback::IInputThreadCallback}, InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent, }; use crate::bounce_keys_filter::BounceKeysFilter; +use crate::input_filter_thread::InputFilterThread; +use crate::slow_keys_filter::SlowKeysFilter; +use crate::sticky_keys_filter::StickyKeysFilter; use log::{error, info}; use std::sync::{Arc, Mutex, RwLock}; @@ -34,6 +38,7 @@ use std::sync::{Arc, Mutex, RwLock}; pub trait Filter { fn notify_key(&mut self, event: &KeyEvent); fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]); + fn destroy(&mut self); } struct InputFilterState { @@ -49,6 +54,7 @@ pub struct InputFilter { // Access to mutable references to mutable state (includes access to filters, enabled, etc.) is // guarded by Mutex for thread safety state: Mutex<InputFilterState>, + input_filter_thread: InputFilterThread, } impl Interface for InputFilter {} @@ -66,7 +72,11 @@ impl InputFilter { first_filter: Box<dyn Filter + Send + Sync>, callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, ) -> InputFilter { - Self { callbacks, state: Mutex::new(InputFilterState { first_filter, enabled: false }) } + Self { + callbacks: callbacks.clone(), + state: Mutex::new(InputFilterState { first_filter, enabled: false }), + input_filter_thread: InputFilterThread::new(InputFilterThreadCreator::new(callbacks)), + } } } @@ -88,16 +98,36 @@ impl IInputFilter for InputFilter { } fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> { - let mut state = self.state.lock().unwrap(); - let mut first_filter: Box<dyn Filter + Send + Sync> = - Box::new(BaseFilter::new(self.callbacks.clone())); - if config.bounceKeysThresholdNs > 0 { - first_filter = - Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs)); - state.enabled = true; - info!("Bounce keys filter is installed"); + { + let mut state = self.state.lock().unwrap(); + state.first_filter.destroy(); + let mut first_filter: Box<dyn Filter + Send + Sync> = + Box::new(BaseFilter::new(self.callbacks.clone())); + if config.stickyKeysEnabled { + first_filter = Box::new(StickyKeysFilter::new( + first_filter, + ModifierStateListener::new(self.callbacks.clone()), + )); + state.enabled = true; + info!("Sticky keys filter is installed"); + } + if config.slowKeysThresholdNs > 0 { + first_filter = Box::new(SlowKeysFilter::new( + first_filter, + config.slowKeysThresholdNs, + self.input_filter_thread.clone(), + )); + state.enabled = true; + info!("Slow keys filter is installed"); + } + if config.bounceKeysThresholdNs > 0 { + first_filter = + Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs)); + state.enabled = true; + info!("Bounce keys filter is installed"); + } + state.first_filter = first_filter; } - state.first_filter = first_filter; Result::Ok(()) } } @@ -123,36 +153,69 @@ impl Filter for BaseFilter { fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) { // do nothing } + + fn destroy(&mut self) { + // do nothing + } +} + +/// This struct wraps around IInputFilterCallbacks restricting access to only +/// {@code onModifierStateChanged()} method of the callback. +#[derive(Clone)] +pub struct ModifierStateListener(Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>); + +impl ModifierStateListener { + pub fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> ModifierStateListener { + Self(callbacks) + } + + pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) { + let _ = self + .0 + .read() + .unwrap() + .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32); + } +} + +/// This struct wraps around IInputFilterCallbacks restricting access to only +/// {@code createInputFilterThread()} method of the callback. +#[derive(Clone)] +pub struct InputFilterThreadCreator(Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>); + +impl InputFilterThreadCreator { + pub fn new( + callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, + ) -> InputFilterThreadCreator { + Self(callbacks) + } + + pub fn create( + &self, + input_thread_callback: &Strong<dyn IInputThreadCallback>, + ) -> Strong<dyn IInputThread> { + self.0.read().unwrap().createInputFilterThread(input_thread_callback).unwrap() + } } #[cfg(test)] mod tests { - use crate::input_filter::{test_filter::TestFilter, Filter, InputFilter}; + use crate::input_filter::{ + test_callbacks::TestCallbacks, test_filter::TestFilter, InputFilter, + }; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; - use binder::{Interface, Strong}; + use binder::Strong; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, IInputFilter::IInputFilter, - IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; use std::sync::{Arc, RwLock}; - struct FakeCallbacks {} - - impl Interface for FakeCallbacks {} - - impl IInputFilterCallbacks for FakeCallbacks { - fn sendKeyEvent(&self, _event: &KeyEvent) -> binder::Result<()> { - Result::Ok(()) - } - } - #[test] fn test_not_enabled_with_default_filter() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); let result = input_filter.isEnabled(); assert!(result.is_ok()); assert!(!result.unwrap()); @@ -160,17 +223,21 @@ mod tests { #[test] fn test_notify_key_with_no_filters() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks.clone()))); let event = create_key_event(); assert!(input_filter.notifyKey(&event).is_ok()); + assert_eq!(test_callbacks.last_event().unwrap(), event); } #[test] fn test_notify_key_with_filter() { let test_filter = TestFilter::new(); - let input_filter = create_input_filter(Box::new(test_filter.clone())); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::create_input_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), + ); let event = create_key_event(); assert!(input_filter.notifyKey(&event).is_ok()); assert_eq!(test_filter.last_event().unwrap(), event); @@ -179,7 +246,11 @@ mod tests { #[test] fn test_notify_devices_changed() { let test_filter = TestFilter::new(); - let input_filter = create_input_filter(Box::new(test_filter.clone())); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::create_input_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), + ); assert!(input_filter .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }]) .is_ok()); @@ -188,21 +259,57 @@ mod tests { #[test] fn test_notify_configuration_changed_enabled_bounce_keys() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); - let result = input_filter - .notifyConfigurationChanged(&InputFilterConfiguration { bounceKeysThresholdNs: 100 }); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); + let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { + bounceKeysThresholdNs: 100, + ..Default::default() + }); + assert!(result.is_ok()); + let result = input_filter.isEnabled(); + assert!(result.is_ok()); + assert!(result.unwrap()); + } + + #[test] + fn test_notify_configuration_changed_enabled_sticky_keys() { + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); + let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { + stickyKeysEnabled: true, + ..Default::default() + }); + assert!(result.is_ok()); + let result = input_filter.isEnabled(); + assert!(result.is_ok()); + assert!(result.unwrap()); + } + + #[test] + fn test_notify_configuration_changed_enabled_slow_keys() { + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); + let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { + slowKeysThresholdNs: 100, + ..Default::default() + }); assert!(result.is_ok()); let result = input_filter.isEnabled(); assert!(result.is_ok()); assert!(result.unwrap()); } - fn create_input_filter(filter: Box<dyn Filter + Send + Sync>) -> InputFilter { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - InputFilter::create_input_filter(filter, Arc::new(RwLock::new(fake_callbacks))) + #[test] + fn test_notify_configuration_changed_destroys_existing_filters() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::create_input_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), + ); + let _ = input_filter + .notifyConfigurationChanged(&InputFilterConfiguration { ..Default::default() }); + assert!(test_filter.is_destroy_called()); } fn create_key_event() -> KeyEvent { @@ -236,6 +343,7 @@ pub mod test_filter { struct TestFilterInner { is_device_changed_called: bool, last_event: Option<KeyEvent>, + is_destroy_called: bool, } #[derive(Default, Clone)] @@ -261,6 +369,10 @@ pub mod test_filter { pub fn is_device_changed_called(&self) -> bool { self.0.read().unwrap().is_device_changed_called } + + pub fn is_destroy_called(&self) -> bool { + self.0.read().unwrap().is_destroy_called + } } impl Filter for TestFilter { @@ -270,5 +382,128 @@ pub mod test_filter { fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) { self.inner().is_device_changed_called = true; } + fn destroy(&mut self) { + self.inner().is_destroy_called = true; + } + } +} + +#[cfg(test)] +pub mod test_callbacks { + use binder::{BinderFeatures, Interface, Strong}; + use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, + IInputThread::{BnInputThread, IInputThread, IInputThreadCallback::IInputThreadCallback}, + KeyEvent::KeyEvent, + }; + use std::sync::{Arc, RwLock, RwLockWriteGuard}; + + #[derive(Default)] + struct TestCallbacksInner { + last_modifier_state: u32, + last_locked_modifier_state: u32, + last_event: Option<KeyEvent>, + test_thread: Option<TestThread>, + } + + #[derive(Default, Clone)] + pub struct TestCallbacks(Arc<RwLock<TestCallbacksInner>>); + + impl Interface for TestCallbacks {} + + impl TestCallbacks { + pub fn new() -> Self { + Default::default() + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestCallbacksInner> { + self.0.write().unwrap() + } + + pub fn last_event(&self) -> Option<KeyEvent> { + self.0.read().unwrap().last_event + } + + pub fn clear(&mut self) { + self.inner().last_event = None; + self.inner().last_modifier_state = 0; + self.inner().last_locked_modifier_state = 0; + } + + pub fn get_last_modifier_state(&self) -> u32 { + self.0.read().unwrap().last_modifier_state + } + + pub fn get_last_locked_modifier_state(&self) -> u32 { + self.0.read().unwrap().last_locked_modifier_state + } + + pub fn is_thread_created(&self) -> bool { + self.0.read().unwrap().test_thread.is_some() + } + + pub fn is_thread_finished(&self) -> bool { + if let Some(test_thread) = &self.0.read().unwrap().test_thread { + return test_thread.is_finish_called(); + } + false + } + } + + impl IInputFilterCallbacks for TestCallbacks { + fn sendKeyEvent(&self, event: &KeyEvent) -> binder::Result<()> { + self.inner().last_event = Some(*event); + Result::Ok(()) + } + + fn onModifierStateChanged( + &self, + modifier_state: i32, + locked_modifier_state: i32, + ) -> std::result::Result<(), binder::Status> { + self.inner().last_modifier_state = modifier_state as u32; + self.inner().last_locked_modifier_state = locked_modifier_state as u32; + Result::Ok(()) + } + + fn createInputFilterThread( + &self, + _callback: &Strong<dyn IInputThreadCallback>, + ) -> std::result::Result<Strong<dyn IInputThread>, binder::Status> { + let test_thread = TestThread::new(); + self.inner().test_thread = Some(test_thread.clone()); + Result::Ok(BnInputThread::new_binder(test_thread, BinderFeatures::default())) + } + } + + #[derive(Default)] + struct TestThreadInner { + is_finish_called: bool, + } + + #[derive(Default, Clone)] + struct TestThread(Arc<RwLock<TestThreadInner>>); + + impl Interface for TestThread {} + + impl TestThread { + pub fn new() -> Self { + Default::default() + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> { + self.0.write().unwrap() + } + + pub fn is_finish_called(&self) -> bool { + self.0.read().unwrap().is_finish_called + } + } + + impl IInputThread for TestThread { + fn finish(&self) -> binder::Result<()> { + self.inner().is_finish_called = true; + Result::Ok(()) + } } } diff --git a/services/inputflinger/rust/input_filter_thread.rs b/services/inputflinger/rust/input_filter_thread.rs new file mode 100644 index 0000000000..2d503aee70 --- /dev/null +++ b/services/inputflinger/rust/input_filter_thread.rs @@ -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. + */ + +//! Input filter thread implementation in rust. +//! Using IInputFilter.aidl interface to create ever looping thread with JNI support, rest of +//! thread handling is done from rust side. +//! +//! NOTE: Tried using rust provided threading infrastructure but that uses std::thread which doesn't +//! have JNI support and can't call into Java policy that we use currently. libutils provided +//! Thread.h also recommends against using std::thread and using the provided infrastructure that +//! already provides way of attaching JniEnv to the created thread. So, we are using an AIDL +//! interface to expose the InputThread infrastructure to rust. + +use crate::input_filter::InputFilterThreadCreator; +use binder::{BinderFeatures, Interface, Strong}; +use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputThread::{ + IInputThread, IInputThreadCallback::BnInputThreadCallback, + IInputThreadCallback::IInputThreadCallback, +}; +use log::{debug, error}; +use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId}; +use std::sync::{Arc, RwLock, RwLockWriteGuard}; +use std::time::Duration; +use std::{thread, thread::Thread}; + +/// Interface to receive callback from Input filter thread +pub trait ThreadCallback { + /// Calls back after the requested timeout expires. + /// {@see InputFilterThread.request_timeout_at_time(...)} + /// + /// NOTE: In case of multiple requests, the timeout request which is earliest in time, will be + /// fulfilled and notified to all the listeners. It's up to the listeners to re-request another + /// timeout in the future. + fn notify_timeout_expired(&self, when_nanos: i64); + /// Unique name for the listener, which will be used to uniquely identify the listener. + fn name(&self) -> &str; +} + +#[derive(Clone)] +pub struct InputFilterThread { + thread_creator: InputFilterThreadCreator, + thread_callback_handler: ThreadCallbackHandler, + inner: Arc<RwLock<InputFilterThreadInner>>, +} + +struct InputFilterThreadInner { + cpp_thread: Option<Strong<dyn IInputThread>>, + looper: Option<Thread>, + next_timeout: i64, + is_finishing: bool, +} + +impl InputFilterThread { + /// Create a new InputFilterThread instance. + /// NOTE: This will create a new thread. Clone the existing instance to reuse the same thread. + pub fn new(thread_creator: InputFilterThreadCreator) -> InputFilterThread { + Self { + thread_creator, + thread_callback_handler: ThreadCallbackHandler::new(), + inner: Arc::new(RwLock::new(InputFilterThreadInner { + cpp_thread: None, + looper: None, + next_timeout: i64::MAX, + is_finishing: false, + })), + } + } + + /// Listener requesting a timeout in future will receive a callback at or before the requested + /// time on the input filter thread. + /// {@see ThreadCallback.notify_timeout_expired(...)} + pub fn request_timeout_at_time(&self, when_nanos: i64) { + let filter_thread = &mut self.filter_thread(); + if when_nanos < filter_thread.next_timeout { + filter_thread.next_timeout = when_nanos; + if let Some(looper) = &filter_thread.looper { + looper.unpark(); + } + } + } + + /// Registers a callback listener. + /// + /// NOTE: If a listener with the same name already exists when registering using + /// {@see InputFilterThread.register_thread_callback(...)}, we will ignore the listener. You + /// must clear any previously registered listeners using + /// {@see InputFilterThread.unregister_thread_callback(...) before registering the new listener. + /// + /// NOTE: Also, registering a callback will start the looper if not already started. + pub fn register_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) { + self.thread_callback_handler.register_thread_callback(callback); + self.start(); + } + + /// Unregisters a callback listener. + /// + /// NOTE: Unregistering a callback will stop the looper if not other callback registered. + pub fn unregister_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) { + self.thread_callback_handler.unregister_thread_callback(callback); + // Stop the thread if no registered callbacks exist. We will recreate the thread when new + // callbacks are registered. + let has_callbacks = self.thread_callback_handler.has_callbacks(); + if !has_callbacks { + self.stop(); + } + } + + fn start(&self) { + debug!("InputFilterThread: start thread"); + let filter_thread = &mut self.filter_thread(); + if filter_thread.cpp_thread.is_none() { + filter_thread.cpp_thread = Some(self.thread_creator.create( + &BnInputThreadCallback::new_binder(self.clone(), BinderFeatures::default()), + )); + filter_thread.looper = None; + filter_thread.is_finishing = false; + } + } + + fn stop(&self) { + debug!("InputFilterThread: stop thread"); + let filter_thread = &mut self.filter_thread(); + filter_thread.is_finishing = true; + if let Some(looper) = &filter_thread.looper { + looper.unpark(); + } + if let Some(cpp_thread) = &filter_thread.cpp_thread { + let _ = cpp_thread.finish(); + } + // Clear all references + filter_thread.cpp_thread = None; + filter_thread.looper = None; + } + + fn loop_once(&self, now: i64) { + let mut wake_up_time = i64::MAX; + let mut timeout_expired = false; + { + // acquire thread lock + let filter_thread = &mut self.filter_thread(); + if filter_thread.is_finishing { + // Thread is finishing so don't block processing on it and let it loop. + return; + } + if filter_thread.next_timeout != i64::MAX { + if filter_thread.next_timeout <= now { + timeout_expired = true; + filter_thread.next_timeout = i64::MAX; + } else { + wake_up_time = filter_thread.next_timeout; + } + } + if filter_thread.looper.is_none() { + filter_thread.looper = Some(std::thread::current()); + } + } // release thread lock + if timeout_expired { + self.thread_callback_handler.notify_timeout_expired(now); + } + if wake_up_time == i64::MAX { + thread::park(); + } else { + let duration_now = Duration::from_nanos(now as u64); + let duration_wake_up = Duration::from_nanos(wake_up_time as u64); + thread::park_timeout(duration_wake_up - duration_now); + } + } + + fn filter_thread(&self) -> RwLockWriteGuard<'_, InputFilterThreadInner> { + self.inner.write().unwrap() + } +} + +impl Interface for InputFilterThread {} + +impl IInputThreadCallback for InputFilterThread { + fn loopOnce(&self) -> binder::Result<()> { + self.loop_once(clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds()); + Result::Ok(()) + } +} + +#[derive(Default, Clone)] +struct ThreadCallbackHandler(Arc<RwLock<ThreadCallbackHandlerInner>>); + +#[derive(Default)] +struct ThreadCallbackHandlerInner { + callbacks: Vec<Box<dyn ThreadCallback + Send + Sync>>, +} + +impl ThreadCallbackHandler { + fn new() -> Self { + Default::default() + } + + fn has_callbacks(&self) -> bool { + !&self.0.read().unwrap().callbacks.is_empty() + } + + fn register_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) { + let callbacks = &mut self.0.write().unwrap().callbacks; + if callbacks.iter().any(|x| x.name() == callback.name()) { + error!( + "InputFilterThread: register_thread_callback, callback {:?} already exists!", + callback.name() + ); + return; + } + debug!( + "InputFilterThread: register_thread_callback, callback {:?} added!", + callback.name() + ); + callbacks.push(callback); + } + + fn unregister_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) { + let callbacks = &mut self.0.write().unwrap().callbacks; + if let Some(index) = callbacks.iter().position(|x| x.name() == callback.name()) { + callbacks.remove(index); + debug!( + "InputFilterThread: unregister_thread_callback, callback {:?} removed!", + callback.name() + ); + return; + } + error!( + "InputFilterThread: unregister_thread_callback, callback {:?} doesn't exist", + callback.name() + ); + } + + fn notify_timeout_expired(&self, when_nanos: i64) { + let callbacks = &self.0.read().unwrap().callbacks; + for callback in callbacks.iter() { + callback.notify_timeout_expired(when_nanos); + } + } +} + +#[cfg(test)] +mod tests { + use crate::input_filter::test_callbacks::TestCallbacks; + use crate::input_filter_thread::{ + test_thread::TestThread, test_thread_callback::TestThreadCallback, + }; + + #[test] + fn test_register_callback_creates_cpp_thread() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let test_thread_callback = TestThreadCallback::new(); + test_thread.register_thread_callback(test_thread_callback); + assert!(test_callbacks.is_thread_created()); + } + + #[test] + fn test_unregister_callback_finishes_cpp_thread() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let test_thread_callback = TestThreadCallback::new(); + test_thread.register_thread_callback(test_thread_callback.clone()); + test_thread.unregister_thread_callback(test_thread_callback); + assert!(test_callbacks.is_thread_finished()); + } + + #[test] + fn test_notify_timeout_called_after_timeout_expired() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let test_thread_callback = TestThreadCallback::new(); + test_thread.register_thread_callback(test_thread_callback.clone()); + test_thread.start_looper(); + + test_thread.request_timeout_at_time(500); + test_thread.dispatch_next(); + + test_thread.move_time_forward(500); + + test_thread.stop_looper(); + assert!(test_thread_callback.is_notify_timeout_called()); + } + + #[test] + fn test_notify_timeout_not_called_before_timeout_expired() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let test_thread_callback = TestThreadCallback::new(); + test_thread.register_thread_callback(test_thread_callback.clone()); + test_thread.start_looper(); + + test_thread.request_timeout_at_time(500); + test_thread.dispatch_next(); + + test_thread.move_time_forward(100); + + test_thread.stop_looper(); + assert!(!test_thread_callback.is_notify_timeout_called()); + } +} + +#[cfg(test)] +pub mod test_thread { + + use crate::input_filter::{test_callbacks::TestCallbacks, InputFilterThreadCreator}; + use crate::input_filter_thread::{test_thread_callback::TestThreadCallback, InputFilterThread}; + use binder::Strong; + use std::sync::{ + atomic::AtomicBool, atomic::AtomicI64, atomic::Ordering, Arc, RwLock, RwLockWriteGuard, + }; + use std::time::Duration; + + #[derive(Clone)] + pub struct TestThread { + input_thread: InputFilterThread, + inner: Arc<RwLock<TestThreadInner>>, + exit_flag: Arc<AtomicBool>, + now: Arc<AtomicI64>, + } + + struct TestThreadInner { + join_handle: Option<std::thread::JoinHandle<()>>, + } + + impl TestThread { + pub fn new(callbacks: TestCallbacks) -> TestThread { + Self { + input_thread: InputFilterThread::new(InputFilterThreadCreator::new(Arc::new( + RwLock::new(Strong::new(Box::new(callbacks))), + ))), + inner: Arc::new(RwLock::new(TestThreadInner { join_handle: None })), + exit_flag: Arc::new(AtomicBool::new(false)), + now: Arc::new(AtomicI64::new(0)), + } + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> { + self.inner.write().unwrap() + } + + pub fn get_input_thread(&self) -> InputFilterThread { + self.input_thread.clone() + } + + pub fn register_thread_callback(&self, thread_callback: TestThreadCallback) { + self.input_thread.register_thread_callback(Box::new(thread_callback)); + } + + pub fn unregister_thread_callback(&self, thread_callback: TestThreadCallback) { + self.input_thread.unregister_thread_callback(Box::new(thread_callback)); + } + + pub fn start_looper(&self) { + self.exit_flag.store(false, Ordering::Relaxed); + let clone = self.clone(); + let join_handle = std::thread::Builder::new() + .name("test_thread".to_string()) + .spawn(move || { + while !clone.exit_flag.load(Ordering::Relaxed) { + clone.loop_once(); + } + }) + .unwrap(); + self.inner().join_handle = Some(join_handle); + // Sleep until the looper thread starts + std::thread::sleep(Duration::from_millis(10)); + } + + pub fn stop_looper(&self) { + self.exit_flag.store(true, Ordering::Relaxed); + { + let mut inner = self.inner(); + if let Some(join_handle) = &inner.join_handle { + join_handle.thread().unpark(); + } + inner.join_handle.take().map(std::thread::JoinHandle::join); + inner.join_handle = None; + } + self.exit_flag.store(false, Ordering::Relaxed); + } + + pub fn move_time_forward(&self, value: i64) { + let _ = self.now.fetch_add(value, Ordering::Relaxed); + self.dispatch_next(); + } + + pub fn dispatch_next(&self) { + if let Some(join_handle) = &self.inner().join_handle { + join_handle.thread().unpark(); + } + // Sleep until the looper thread runs a loop + std::thread::sleep(Duration::from_millis(10)); + } + + fn loop_once(&self) { + self.input_thread.loop_once(self.now.load(Ordering::Relaxed)); + } + + pub fn request_timeout_at_time(&self, when_nanos: i64) { + self.input_thread.request_timeout_at_time(when_nanos); + } + } +} + +#[cfg(test)] +pub mod test_thread_callback { + use crate::input_filter_thread::ThreadCallback; + use std::sync::{Arc, RwLock, RwLockWriteGuard}; + + #[derive(Default)] + struct TestThreadCallbackInner { + is_notify_timeout_called: bool, + } + + #[derive(Default, Clone)] + pub struct TestThreadCallback(Arc<RwLock<TestThreadCallbackInner>>); + + impl TestThreadCallback { + pub fn new() -> Self { + Default::default() + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestThreadCallbackInner> { + self.0.write().unwrap() + } + + pub fn is_notify_timeout_called(&self) -> bool { + self.0.read().unwrap().is_notify_timeout_called + } + } + + impl ThreadCallback for TestThreadCallback { + fn notify_timeout_expired(&self, _when_nanos: i64) { + self.inner().is_notify_timeout_called = true; + } + fn name(&self) -> &str { + "TestThreadCallback" + } + } +} diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs index 5abef68a75..4af7b84a21 100644 --- a/services/inputflinger/rust/lib.rs +++ b/services/inputflinger/rust/lib.rs @@ -21,6 +21,9 @@ mod bounce_keys_filter; mod input_filter; +mod input_filter_thread; +mod slow_keys_filter; +mod sticky_keys_filter; use crate::input_filter::InputFilter; use binder::{ @@ -62,9 +65,9 @@ mod ffi { /// /// # Safety /// -/// This function is safe iff `callback` is a valid pointer to an `AIBinder` interface of type -/// `IInputFlingerRustBootstrapCallback`. The pointer must have had its reference count manually -/// incremented using `AIBinder_incStrong`. See `binder::unstable_api::new_spibinder`. +/// The provided `callback` must be a valid pointer to an `AIBinder` interface of type +/// `IInputFlingerRustBootstrapCallback`, and the caller must give this function ownership of one +/// strong refcount to the interface. See `binder::unstable_api::new_spibinder`. unsafe fn create_inputflinger_rust(callback: *mut ffi::IInputFlingerRustBootstrapCallbackAIBinder) { logger::init( logger::Config::default() diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs new file mode 100644 index 0000000000..09fbf40842 --- /dev/null +++ b/services/inputflinger/rust/slow_keys_filter.rs @@ -0,0 +1,392 @@ +/* + * 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. + */ + +//! Slow keys input filter implementation. +//! Slow keys is an accessibility feature to aid users who have physical disabilities, that allows +//! the user to specify the duration for which one must press-and-hold a key before the system +//! accepts the keypress. +use crate::input_filter::Filter; +use crate::input_filter_thread::{InputFilterThread, ThreadCallback}; +use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; +use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, +}; +use log::debug; +use std::collections::HashSet; +use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; + +// Policy flags from Input.h +const POLICY_FLAG_DISABLE_KEY_REPEAT: i32 = 0x08000000; + +#[derive(Debug)] +struct OngoingKeyDown { + scancode: i32, + device_id: i32, + down_time: i64, +} + +struct SlowKeysFilterInner { + next: Box<dyn Filter + Send + Sync>, + slow_key_threshold_ns: i64, + external_devices: HashSet<i32>, + // This tracks KeyEvents that are blocked by Slow keys filter and will be passed through if the + // press duration exceeds the slow keys threshold. + pending_down_events: Vec<KeyEvent>, + // This tracks KeyEvent streams that have press duration greater than the slow keys threshold, + // hence any future ACTION_DOWN (if repeats are handled on HW side) or ACTION_UP are allowed to + // pass through without waiting. + ongoing_down_events: Vec<OngoingKeyDown>, + input_filter_thread: InputFilterThread, +} + +#[derive(Clone)] +pub struct SlowKeysFilter(Arc<RwLock<SlowKeysFilterInner>>); + +impl SlowKeysFilter { + /// Create a new SlowKeysFilter instance. + pub fn new( + next: Box<dyn Filter + Send + Sync>, + slow_key_threshold_ns: i64, + input_filter_thread: InputFilterThread, + ) -> SlowKeysFilter { + let filter = Self(Arc::new(RwLock::new(SlowKeysFilterInner { + next, + slow_key_threshold_ns, + external_devices: HashSet::new(), + pending_down_events: Vec::new(), + ongoing_down_events: Vec::new(), + input_filter_thread: input_filter_thread.clone(), + }))); + input_filter_thread.register_thread_callback(Box::new(filter.clone())); + filter + } + + fn read_inner(&self) -> RwLockReadGuard<'_, SlowKeysFilterInner> { + self.0.read().unwrap() + } + + fn write_inner(&self) -> RwLockWriteGuard<'_, SlowKeysFilterInner> { + self.0.write().unwrap() + } + + fn request_next_callback(&self) { + let slow_filter = &self.read_inner(); + if slow_filter.pending_down_events.is_empty() { + return; + } + if let Some(event) = slow_filter.pending_down_events.iter().min_by_key(|x| x.downTime) { + slow_filter.input_filter_thread.request_timeout_at_time(event.downTime); + } + } +} + +impl Filter for SlowKeysFilter { + fn notify_key(&mut self, event: &KeyEvent) { + { + // acquire write lock + let mut slow_filter = self.write_inner(); + if !(slow_filter.external_devices.contains(&event.deviceId) + && event.source == Source::KEYBOARD) + { + slow_filter.next.notify_key(event); + return; + } + // Pass all events through if key down has already been processed + // Do update the downtime before sending the events through + if let Some(index) = slow_filter + .ongoing_down_events + .iter() + .position(|x| x.device_id == event.deviceId && x.scancode == event.scanCode) + { + let mut new_event = *event; + new_event.downTime = slow_filter.ongoing_down_events[index].down_time; + slow_filter.next.notify_key(&new_event); + if event.action == KeyEventAction::UP { + slow_filter.ongoing_down_events.remove(index); + } + return; + } + match event.action { + KeyEventAction::DOWN => { + if slow_filter + .pending_down_events + .iter() + .any(|x| x.deviceId == event.deviceId && x.scanCode == event.scanCode) + { + debug!("Dropping key down event since another pending down event exists"); + return; + } + let mut pending_event = *event; + pending_event.downTime += slow_filter.slow_key_threshold_ns; + pending_event.eventTime = pending_event.downTime; + // Currently a slow keys user ends up repeating the presses key quite often + // since default repeat thresholds are very low, so blocking repeat for events + // when slow keys is enabled. + // TODO(b/322327461): Allow key repeat with slow keys, once repeat key rate and + // thresholds can be modified in the settings. + pending_event.policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; + slow_filter.pending_down_events.push(pending_event); + } + KeyEventAction::UP => { + debug!("Dropping key up event due to insufficient press duration"); + if let Some(index) = slow_filter + .pending_down_events + .iter() + .position(|x| x.deviceId == event.deviceId && x.scanCode == event.scanCode) + { + slow_filter.pending_down_events.remove(index); + } + } + _ => (), + } + } // release write lock + self.request_next_callback(); + } + + fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) { + let mut slow_filter = self.write_inner(); + slow_filter + .pending_down_events + .retain(|event| device_infos.iter().any(|x| event.deviceId == x.deviceId)); + slow_filter + .ongoing_down_events + .retain(|event| device_infos.iter().any(|x| event.device_id == x.deviceId)); + slow_filter.external_devices.clear(); + for device_info in device_infos { + if device_info.external { + slow_filter.external_devices.insert(device_info.deviceId); + } + } + slow_filter.next.notify_devices_changed(device_infos); + } + + fn destroy(&mut self) { + let mut slow_filter = self.write_inner(); + slow_filter.input_filter_thread.unregister_thread_callback(Box::new(self.clone())); + slow_filter.next.destroy(); + } +} + +impl ThreadCallback for SlowKeysFilter { + fn notify_timeout_expired(&self, when_nanos: i64) { + { + // acquire write lock + let slow_filter = &mut self.write_inner(); + for event in slow_filter.pending_down_events.clone() { + if event.downTime <= when_nanos { + slow_filter.next.notify_key(&event); + slow_filter.ongoing_down_events.push(OngoingKeyDown { + scancode: event.scanCode, + device_id: event.deviceId, + down_time: event.downTime, + }); + } + } + slow_filter.pending_down_events.retain(|event| event.downTime > when_nanos); + } // release write lock + self.request_next_callback(); + } + + fn name(&self) -> &str { + "slow_keys_filter" + } +} + +#[cfg(test)] +mod tests { + use crate::input_filter::{test_callbacks::TestCallbacks, test_filter::TestFilter, Filter}; + use crate::input_filter_thread::test_thread::TestThread; + use crate::slow_keys_filter::{SlowKeysFilter, POLICY_FLAG_DISABLE_KEY_REPEAT}; + use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; + use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, + }; + + static BASE_KEY_EVENT: KeyEvent = KeyEvent { + id: 1, + deviceId: 1, + downTime: 0, + readTime: 0, + eventTime: 0, + source: Source::KEYBOARD, + displayId: 0, + policyFlags: 0, + action: KeyEventAction::DOWN, + flags: 0, + keyCode: 1, + scanCode: 0, + metaState: 0, + }; + + #[test] + fn test_is_notify_key_for_internal_keyboard_not_blocked() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_internal_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_for_external_stylus_not_blocked() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + let event = + KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_notify_key_for_external_keyboard_when_key_pressed_for_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }); + assert!(next.last_event().is_none()); + test_thread.dispatch_next(); + + test_thread.move_time_forward(100); + + test_thread.stop_looper(); + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::DOWN, + downTime: 100, + eventTime: 100, + policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT, + ..BASE_KEY_EVENT + } + ); + } + + #[test] + fn test_notify_key_for_external_keyboard_when_key_not_pressed_for_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }); + test_thread.dispatch_next(); + + test_thread.move_time_forward(10); + + filter.notify_key(&KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT }); + test_thread.dispatch_next(); + + test_thread.stop_looper(); + assert!(next.last_event().is_none()); + } + + #[test] + fn test_notify_key_for_external_keyboard_when_device_removed_before_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }); + assert!(next.last_event().is_none()); + test_thread.dispatch_next(); + + filter.notify_devices_changed(&[]); + test_thread.dispatch_next(); + + test_thread.move_time_forward(100); + + test_thread.stop_looper(); + assert!(next.last_event().is_none()); + } + + fn setup_filter_with_external_device( + next: Box<dyn Filter + Send + Sync>, + test_thread: TestThread, + device_id: i32, + threshold: i64, + ) -> SlowKeysFilter { + setup_filter_with_devices( + next, + test_thread, + &[DeviceInfo { deviceId: device_id, external: true }], + threshold, + ) + } + + fn setup_filter_with_internal_device( + next: Box<dyn Filter + Send + Sync>, + test_thread: TestThread, + device_id: i32, + threshold: i64, + ) -> SlowKeysFilter { + setup_filter_with_devices( + next, + test_thread, + &[DeviceInfo { deviceId: device_id, external: false }], + threshold, + ) + } + + fn setup_filter_with_devices( + next: Box<dyn Filter + Send + Sync>, + test_thread: TestThread, + devices: &[DeviceInfo], + threshold: i64, + ) -> SlowKeysFilter { + let mut filter = SlowKeysFilter::new(next, threshold, test_thread.get_input_thread()); + filter.notify_devices_changed(devices); + filter + } +} diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs new file mode 100644 index 0000000000..6c2277c813 --- /dev/null +++ b/services/inputflinger/rust/sticky_keys_filter.rs @@ -0,0 +1,519 @@ +/* + * Copyright 2023 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. + */ + +//! Sticky keys input filter implementation. +//! Sticky keys is an accessibility feature that assists users who have physical disabilities or +//! helps users reduce repetitive strain injury. It serializes keystrokes instead of pressing +//! multiple keys at a time, allowing the user to press and release a modifier key, such as Shift, +//! Ctrl, Alt, or any other modifier key, and have it remain active until any other key is pressed. +use crate::input_filter::{Filter, ModifierStateListener}; +use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, +}; +use std::collections::HashSet; + +// Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h +const KEYCODE_ALT_LEFT: i32 = 57; +const KEYCODE_ALT_RIGHT: i32 = 58; +const KEYCODE_SHIFT_LEFT: i32 = 59; +const KEYCODE_SHIFT_RIGHT: i32 = 60; +const KEYCODE_SYM: i32 = 63; +const KEYCODE_CTRL_LEFT: i32 = 113; +const KEYCODE_CTRL_RIGHT: i32 = 114; +const KEYCODE_CAPS_LOCK: i32 = 115; +const KEYCODE_SCROLL_LOCK: i32 = 116; +const KEYCODE_META_LEFT: i32 = 117; +const KEYCODE_META_RIGHT: i32 = 118; +const KEYCODE_FUNCTION: i32 = 119; +const KEYCODE_NUM_LOCK: i32 = 143; + +// Modifier states: values are from /frameworks/native/include/android/input.h +const META_ALT_ON: u32 = 0x02; +const META_ALT_LEFT_ON: u32 = 0x10; +const META_ALT_RIGHT_ON: u32 = 0x20; +const META_SHIFT_ON: u32 = 0x01; +const META_SHIFT_LEFT_ON: u32 = 0x40; +const META_SHIFT_RIGHT_ON: u32 = 0x80; +const META_CTRL_ON: u32 = 0x1000; +const META_CTRL_LEFT_ON: u32 = 0x2000; +const META_CTRL_RIGHT_ON: u32 = 0x4000; +const META_META_ON: u32 = 0x10000; +const META_META_LEFT_ON: u32 = 0x20000; +const META_META_RIGHT_ON: u32 = 0x40000; + +pub struct StickyKeysFilter { + next: Box<dyn Filter + Send + Sync>, + listener: ModifierStateListener, + /// Tracking devices that contributed to the modifier state. + contributing_devices: HashSet<i32>, + /// State describing the current enabled modifiers. This contain both locked and non-locked + /// modifier state bits. + modifier_state: u32, + /// State describing the current locked modifiers. These modifiers will not be cleared on a + /// non-modifier key press. They will be cleared only if the locked modifier key is pressed + /// again. + locked_modifier_state: u32, +} + +impl StickyKeysFilter { + /// Create a new StickyKeysFilter instance. + pub fn new( + next: Box<dyn Filter + Send + Sync>, + listener: ModifierStateListener, + ) -> StickyKeysFilter { + Self { + next, + listener, + contributing_devices: HashSet::new(), + modifier_state: 0, + locked_modifier_state: 0, + } + } +} + +impl Filter for StickyKeysFilter { + fn notify_key(&mut self, event: &KeyEvent) { + let up = event.action == KeyEventAction::UP; + let mut modifier_state = self.modifier_state; + let mut locked_modifier_state = self.locked_modifier_state; + if !is_ephemeral_modifier_key(event.keyCode) { + // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like + // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with + // the KeyEvent. + let old_modifier_state = event.metaState as u32; + let mut new_event = *event; + // Send the current modifier state with the key event before clearing non-locked + // modifier state + new_event.metaState = + (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state) as i32; + self.next.notify_key(&new_event); + if up && !is_modifier_key(event.keyCode) { + modifier_state = + clear_ephemeral_modifier_state(modifier_state) | locked_modifier_state; + } + } else if up { + // Update contributing devices to track keyboards + self.contributing_devices.insert(event.deviceId); + // If ephemeral modifier key, capture the key and update the sticky modifier states + let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode); + let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode); + if locked_modifier_state & modifier_key_mask != 0 { + locked_modifier_state &= !symmetrical_modifier_key_mask; + modifier_state &= !symmetrical_modifier_key_mask; + } else if modifier_key_mask & modifier_state != 0 { + locked_modifier_state |= modifier_key_mask; + modifier_state = + (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask; + } else { + modifier_state |= modifier_key_mask; + } + } + if self.modifier_state != modifier_state + || self.locked_modifier_state != locked_modifier_state + { + self.modifier_state = modifier_state; + self.locked_modifier_state = locked_modifier_state; + self.listener.modifier_state_changed(modifier_state, locked_modifier_state); + } + } + + fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) { + // Clear state if all contributing devices removed + self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId)); + if self.contributing_devices.is_empty() + && (self.modifier_state != 0 || self.locked_modifier_state != 0) + { + self.modifier_state = 0; + self.locked_modifier_state = 0; + self.listener.modifier_state_changed(0, 0); + } + self.next.notify_devices_changed(device_infos); + } + + fn destroy(&mut self) { + self.next.destroy(); + } +} + +fn is_modifier_key(keycode: i32) -> bool { + matches!( + keycode, + KEYCODE_ALT_LEFT + | KEYCODE_ALT_RIGHT + | KEYCODE_SHIFT_LEFT + | KEYCODE_SHIFT_RIGHT + | KEYCODE_CTRL_LEFT + | KEYCODE_CTRL_RIGHT + | KEYCODE_META_LEFT + | KEYCODE_META_RIGHT + | KEYCODE_SYM + | KEYCODE_FUNCTION + | KEYCODE_CAPS_LOCK + | KEYCODE_NUM_LOCK + | KEYCODE_SCROLL_LOCK + ) +} + +fn is_ephemeral_modifier_key(keycode: i32) -> bool { + matches!( + keycode, + KEYCODE_ALT_LEFT + | KEYCODE_ALT_RIGHT + | KEYCODE_SHIFT_LEFT + | KEYCODE_SHIFT_RIGHT + | KEYCODE_CTRL_LEFT + | KEYCODE_CTRL_RIGHT + | KEYCODE_META_LEFT + | KEYCODE_META_RIGHT + ) +} + +fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 { + match keycode { + KEYCODE_ALT_LEFT => META_ALT_LEFT_ON | META_ALT_ON, + KEYCODE_ALT_RIGHT => META_ALT_RIGHT_ON | META_ALT_ON, + KEYCODE_SHIFT_LEFT => META_SHIFT_LEFT_ON | META_SHIFT_ON, + KEYCODE_SHIFT_RIGHT => META_SHIFT_RIGHT_ON | META_SHIFT_ON, + KEYCODE_CTRL_LEFT => META_CTRL_LEFT_ON | META_CTRL_ON, + KEYCODE_CTRL_RIGHT => META_CTRL_RIGHT_ON | META_CTRL_ON, + KEYCODE_META_LEFT => META_META_LEFT_ON | META_META_ON, + KEYCODE_META_RIGHT => META_META_RIGHT_ON | META_META_ON, + _ => 0, + } +} + +/// Modifier mask including both left and right versions of a modifier key. +fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 { + match keycode { + KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON, + KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => { + META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON + } + KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => { + META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON + } + KEYCODE_META_LEFT | KEYCODE_META_RIGHT => { + META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON + } + _ => 0, + } +} + +fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 { + modifier_state + & !(META_ALT_LEFT_ON + | META_ALT_RIGHT_ON + | META_ALT_ON + | META_SHIFT_LEFT_ON + | META_SHIFT_RIGHT_ON + | META_SHIFT_ON + | META_CTRL_LEFT_ON + | META_CTRL_RIGHT_ON + | META_CTRL_ON + | META_META_LEFT_ON + | META_META_RIGHT_ON + | META_META_ON) +} + +#[cfg(test)] +mod tests { + use crate::input_filter::{ + test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, ModifierStateListener, + }; + use crate::sticky_keys_filter::{ + StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK, + KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT, + KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT, + KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, META_ALT_LEFT_ON, META_ALT_ON, META_ALT_RIGHT_ON, + META_CTRL_LEFT_ON, META_CTRL_ON, META_CTRL_RIGHT_ON, META_META_LEFT_ON, META_META_ON, + META_META_RIGHT_ON, META_SHIFT_LEFT_ON, META_SHIFT_ON, META_SHIFT_RIGHT_ON, + }; + use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; + use binder::Strong; + use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, + KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, + }; + use std::sync::{Arc, RwLock}; + + static DEVICE_ID: i32 = 1; + static KEY_A: i32 = 29; + static BASE_KEY_DOWN: KeyEvent = KeyEvent { + id: 1, + deviceId: DEVICE_ID, + downTime: 0, + readTime: 0, + eventTime: 0, + source: Source::KEYBOARD, + displayId: 0, + policyFlags: 0, + action: KeyEventAction::DOWN, + flags: 0, + keyCode: 0, + scanCode: 0, + metaState: 0, + }; + + static BASE_KEY_UP: KeyEvent = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_DOWN }; + + #[test] + fn test_notify_key_consumes_ephemeral_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let key_codes = &[ + KEYCODE_ALT_LEFT, + KEYCODE_ALT_RIGHT, + KEYCODE_CTRL_LEFT, + KEYCODE_CTRL_RIGHT, + KEYCODE_SHIFT_LEFT, + KEYCODE_SHIFT_RIGHT, + KEYCODE_META_LEFT, + KEYCODE_META_RIGHT, + ]; + for key_code in key_codes.iter() { + sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN }); + assert!(test_filter.last_event().is_none()); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_UP }); + assert!(test_filter.last_event().is_none()); + } + } + + #[test] + fn test_notify_key_passes_non_ephemeral_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let key_codes = &[ + KEYCODE_CAPS_LOCK, + KEYCODE_NUM_LOCK, + KEYCODE_SCROLL_LOCK, + KEYCODE_FUNCTION, + KEYCODE_SYM, + ]; + for key_code in key_codes.iter() { + let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + } + } + + #[test] + fn test_notify_key_passes_non_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + + let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + } + + #[test] + fn test_modifier_state_updated_on_modifier_key_press() { + let mut test_filter = TestFilter::new(); + let mut test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let test_states = &[ + (KEYCODE_ALT_LEFT, META_ALT_ON | META_ALT_LEFT_ON), + (KEYCODE_ALT_RIGHT, META_ALT_ON | META_ALT_RIGHT_ON), + (KEYCODE_CTRL_LEFT, META_CTRL_ON | META_CTRL_LEFT_ON), + (KEYCODE_CTRL_RIGHT, META_CTRL_ON | META_CTRL_RIGHT_ON), + (KEYCODE_SHIFT_LEFT, META_SHIFT_ON | META_SHIFT_LEFT_ON), + (KEYCODE_SHIFT_RIGHT, META_SHIFT_ON | META_SHIFT_RIGHT_ON), + (KEYCODE_META_LEFT, META_META_ON | META_META_LEFT_ON), + (KEYCODE_META_RIGHT, META_META_ON | META_META_RIGHT_ON), + ]; + for test_state in test_states.iter() { + test_filter.clear(); + test_callbacks.clear(); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + // Re-send keys to lock it + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1); + + // Re-send keys to clear + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + } + + #[test] + fn test_modifier_state_cleared_on_non_modifier_key_press() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + + #[test] + fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_UP }); + + assert_eq!( + test_callbacks.get_last_modifier_state(), + META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON + ); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + } + + #[test] + fn test_key_events_have_sticky_modifier_state() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + assert_eq!( + test_filter.last_event().unwrap().metaState as u32, + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + assert_eq!( + test_filter.last_event().unwrap().metaState as u32, + META_CTRL_LEFT_ON | META_CTRL_ON + ); + } + + #[test] + fn test_modifier_state_not_cleared_until_all_devices_removed() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 1, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_DOWN + }); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 1, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_UP + }); + + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 2, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_DOWN + }); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 2, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_UP + }); + + sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]); + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_devices_changed(&[]); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + + fn setup_filter( + next: Box<dyn Filter + Send + Sync>, + callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, + ) -> StickyKeysFilter { + StickyKeysFilter::new(next, ModifierStateListener::new(callbacks)) + } +} diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 9c9f643656..09ae6dd28c 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -44,6 +44,7 @@ cc_test { "EventHub_test.cpp", "FakeEventHub.cpp", "FakeInputReaderPolicy.cpp", + "FakeInputTracingBackend.cpp", "FakePointerController.cpp", "FocusResolver_test.cpp", "GestureConverter_test.cpp", @@ -68,6 +69,7 @@ cc_test { "TimerProvider_test.cpp", "TestInputListener.cpp", "TouchpadInputMapper_test.cpp", + "MultiTouchInputMapper_test.cpp", "KeyboardInputMapper_test.cpp", "UinputDevice.cpp", "UnwantedInteractionBlocker_test.cpp", diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index 6d6b7d821c..de740672fc 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -16,14 +16,26 @@ #include "CursorInputMapper.h" +#include <list> +#include <string> +#include <tuple> +#include <variant> + #include <android-base/logging.h> #include <com_android_input_flags.h> #include <gtest/gtest.h> +#include <input/DisplayViewport.h> +#include <linux/input-event-codes.h> +#include <linux/input.h> +#include <utils/Timers.h> #include "FakePointerController.h" #include "InputMapperTest.h" +#include "InputReaderBase.h" #include "InterfaceMocks.h" +#include "NotifyArgs.h" #include "TestEventMatchers.h" +#include "ui/Rotation.h" #define TAG "CursorInputMapper_test" @@ -40,23 +52,97 @@ constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION; constexpr int32_t DISPLAY_ID = 0; +constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1; constexpr int32_t DISPLAY_WIDTH = 480; constexpr int32_t DISPLAY_HEIGHT = 800; -constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified + +constexpr int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + +namespace { + +DisplayViewport createPrimaryViewport(ui::Rotation orientation) { + const bool isRotated = + orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270; + DisplayViewport v; + v.displayId = DISPLAY_ID; + v.orientation = orientation; + v.logicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.logicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.physicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.physicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.deviceWidth = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.deviceHeight = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.isActive = true; + v.uniqueId = "local:1"; + return v; +} + +DisplayViewport createSecondaryViewport() { + DisplayViewport v; + v.displayId = SECONDARY_DISPLAY_ID; + v.orientation = ui::Rotation::Rotation0; + v.logicalRight = DISPLAY_HEIGHT; + v.logicalBottom = DISPLAY_WIDTH; + v.physicalRight = DISPLAY_HEIGHT; + v.physicalBottom = DISPLAY_WIDTH; + v.deviceWidth = DISPLAY_HEIGHT; + v.deviceHeight = DISPLAY_WIDTH; + v.isActive = true; + v.uniqueId = "local:2"; + v.type = ViewportType::EXTERNAL; + return v; +} + +/** + * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper. + * + * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates + * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input + * device doesn't set its associated viewport when it's configured. + * + * TODO(b/319217713): work out a way to avoid this fake. + */ +class ViewportFakingInputDeviceContext : public InputDeviceContext { +public: + ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId, + std::optional<DisplayViewport> viewport) + : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {} + + ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId, + ui::Rotation orientation) + : ViewportFakingInputDeviceContext(device, eventHubId, + createPrimaryViewport(orientation)) {} + + std::optional<DisplayViewport> getAssociatedViewport() const override { + return mAssociatedViewport; + } + + void setViewport(const std::optional<DisplayViewport>& viewport) { + mAssociatedViewport = viewport; + } + +private: + std::optional<DisplayViewport> mAssociatedViewport; +}; + +} // namespace namespace input_flags = com::android::input::flags; /** * Unit tests for CursorInputMapper. - * This class is named 'CursorInputMapperUnitTest' to avoid name collision with the existing - * 'CursorInputMapperTest'. If all of the CursorInputMapper tests are migrated here, the name - * can be simplified to 'CursorInputMapperTest'. - * TODO(b/283812079): move CursorInputMapper tests here. + * These classes are named 'CursorInputMapperUnitTest...' to avoid name collision with the existing + * 'CursorInputMapperTest...' classes. If all of the CursorInputMapper tests are migrated here, the + * name can be simplified to 'CursorInputMapperTest'. + * + * TODO(b/283812079): move the remaining CursorInputMapper tests here. The ones that are left all + * depend on viewport association, for which we'll need to fake InputDeviceContext. */ -class CursorInputMapperUnitTest : public InputMapperUnitTest { +class CursorInputMapperUnitTestBase : public InputMapperUnitTest { protected: - void SetUp() override { - InputMapperUnitTest::SetUp(); + void SetUp() override { SetUpWithBus(BUS_USB); } + void SetUpWithBus(int bus) override { + InputMapperUnitTest::SetUpWithBus(bus); // Current scan code state - all keys are UP by default setScanCodeState(KeyState::UP, @@ -68,11 +154,15 @@ protected: .WillRepeatedly(Return(false)); mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, "local:0", NO_PORT, - ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); + } - mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); + virtual bool isPointerChoreographerEnabled() { return false; } + + void createMapper() { + createDevice(); + mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration, + isPointerChoreographerEnabled()); } void setPointerCapture(bool enabled) { @@ -83,19 +173,57 @@ protected: mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::POINTER_CAPTURE); ASSERT_THAT(args, - ElementsAre( - VariantWith<NotifyDeviceResetArgs>(AllOf(WithDeviceId(DEVICE_ID))))); + ElementsAre(VariantWith<NotifyDeviceResetArgs>( + AllOf(WithDeviceId(DEVICE_ID), WithEventTime(ARBITRARY_TIME))))); // Check that generation also got bumped ASSERT_GT(mDevice->getGeneration(), generation); } + + void testMotionRotation(int32_t originalX, int32_t originalY, int32_t rotatedX, + int32_t rotatedY) { + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, originalX); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, originalY); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(ACTION_MOVE), + WithCoords(float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, + float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD))))); + } +}; + +class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + input_flags::enable_new_mouse_pointer_ballistics(false); + CursorInputMapperUnitTestBase::SetUp(); + } + + bool isPointerChoreographerEnabled() override { return false; } }; +TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsMouseInPointerMode) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + ASSERT_EQ(AINPUT_SOURCE_MOUSE, mMapper->getSources()); +} + +TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsTrackballInNavigationMode) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mMapper->getSources()); +} + /** * Move the mouse and then click the button. Check whether HOVER_EXIT is generated when hovering * ends. Currently, it is not. */ TEST_F(CursorInputMapperUnitTest, HoverAndLeftButtonPress) { + createMapper(); std::list<NotifyArgs> args; // Move the cursor a little @@ -139,6 +267,7 @@ TEST_F(CursorInputMapperUnitTest, HoverAndLeftButtonPress) { * When it's not SOURCE_MOUSE, CursorInputMapper doesn't populate cursor position values. */ TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) { + createMapper(); setPointerCapture(true); std::list<NotifyArgs> args; @@ -196,10 +325,10 @@ TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) { // Disable pointer capture. Afterwards, events should be generated the usual way. setPointerCapture(false); - const auto expectedCoords = input_flags::enable_pointer_choreographer() + const auto expectedCoords = CursorInputMapperUnitTest::isPointerChoreographerEnabled() ? WithCoords(0, 0) : WithCoords(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f); - const auto expectedCursorPosition = input_flags::enable_pointer_choreographer() + const auto expectedCursorPosition = CursorInputMapperUnitTest::isPointerChoreographerEnabled() ? WithCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION) : WithCursorPosition(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f); args.clear(); @@ -213,4 +342,1318 @@ TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) { WithRelativeMotion(10.0f, 20.0f))))); } +TEST_F(CursorInputMapperUnitTest, + PopulateDeviceInfoReturnsRangeFromPointerControllerInPointerMode) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + mFakePolicy->clearViewports(); + mFakePointerController->clearBounds(); + createMapper(); + + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + // Initially there should not be a valid motion range because there's no viewport or pointer + // bounds. + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); + + // When the bounds are set, then there should be a valid motion range. + mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1); + mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); + std::list<NotifyArgs> args = + mMapper->reconfigure(systemTime(), mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_THAT(args, testing::IsEmpty()); + + InputDeviceInfo info2; + mMapper->populateDeviceInfo(info2); + + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 1, + 800 - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 2, + 480 - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsScaledRangeInNavigationMode) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL, + -1.0f, 1.0f, 0.0f, + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL, + -1.0f, 1.0f, 0.0f, + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_TRACKBALL, 0.0f, 1.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldSetAllFieldsAndIncludeGlobalMetaState) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + EXPECT_CALL(mMockInputReaderContext, getGlobalMetaState()) + .WillRepeatedly(Return(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON)); + + std::list<NotifyArgs> args; + + // Button press. + // Mostly testing non x/y behavior here so we don't need to check again elsewhere. + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), + WithEdgeFlags(0), WithPolicyFlags(0), + WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPointerCount(1), WithPointerId(0, 0), + WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f), + WithPressure(1.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))), + VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), + WithEdgeFlags(0), WithPolicyFlags(0), + WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPointerCount(1), WithPointerId(0, 0), + WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f), + WithPressure(1.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))))); + args.clear(); + + // Button release. Should have same down time. + args += process(ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0); + args += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME + 1), + WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), + WithEdgeFlags(0), WithPolicyFlags(0), + WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(0), WithPointerCount(1), + WithPointerId(0, 0), WithToolType(ToolType::MOUSE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))), + VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME + 1), + WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), + WithEdgeFlags(0), WithPolicyFlags(0), + WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(0), WithPointerCount(1), + WithPointerId(0, 0), WithToolType(ToolType::MOUSE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))))); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + std::list<NotifyArgs> args; + + // Motion in X but not Y. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f), + WithPressure(0.0f))))); + args.clear(); + + // Motion in Y but not X. + args += process(ARBITRARY_TIME, EV_REL, REL_Y, -2); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), + WithPressure(0.0f))))); + args.clear(); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentButtonUpdates) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + std::list<NotifyArgs> args; + + // Button press. + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + args.clear(); + + // Button release. + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + std::list<NotifyArgs> args; + + // Combined X, Y and Button. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, -2); + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, + -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, + -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), + WithPressure(1.0f))))); + args.clear(); + + // Move X, Y a bit while pressed. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 2); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(2.0f / TRACKBALL_MOVEMENT_THRESHOLD, + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD), + WithPressure(1.0f))))); + args.clear(); + + // Release Button. + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); + args.clear(); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldNotRotateMotionsWhenOrientationAware) { + // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not + // need to be rotated. + mPropertyMap.addProperty("cursor.mode", "navigation"); + mPropertyMap.addProperty("cursor.orientationAware", "1"); + createDevice(); + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation90); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1)); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAware) { + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + mPropertyMap.addProperty("cursor.mode", "navigation"); + createDevice(); + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation0); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1)); + + deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation90)); + std::list<NotifyArgs> args = + mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1)); + + deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation180)); + args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1)); + + deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation270)); + args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, 1)); +} + +TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesOrientationChanges) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + DisplayViewport viewport = createPrimaryViewport(ui::Rotation::Rotation90); + mFakePointerController->setDisplayViewport(viewport); + mReaderConfiguration.setDisplayViewports({viewport}); + createMapper(); + + // Verify that the coordinates are rotated. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithRelativeMotion(-20.0f, 10.0f))))); + + // Enable Pointer Capture. + setPointerCapture(true); + + // Move and verify rotation is not applied. + args = process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(ACTION_MOVE), + WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), + WithCoords(10.0f, 20.0f))))); +} + +TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the secondary display as the display on which the pointer should be shown. The + // InputDevice is not associated with any display. + mFakePointerController->setDisplayViewport(secondaryViewport); + mFakePointerController->setPosition(100, 200); + createMapper(); + + // Ensure input events are generated for the secondary display. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))))); + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); +} + +TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdWithAssociatedViewport) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the secondary display as the display on which the pointer should be shown. + mFakePointerController->setDisplayViewport(secondaryViewport); + mFakePointerController->setPosition(100, 200); + createDevice(); + // Associate the InputDevice with the secondary display. + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); + mMapper = createInputMapper< + CursorInputMapper>(deviceContext, mReaderConfiguration, + CursorInputMapperUnitTest::isPointerChoreographerEnabled()); + + // Ensure input events are generated for the secondary display. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))))); + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); +} + +TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdIgnoresEventsForMismatchedPointerDisplay) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the primary display as the display on which the pointer should be shown. + mFakePointerController->setDisplayViewport(primaryViewport); + createDevice(); + // Associate the InputDevice with the secondary display. + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); + mMapper = createInputMapper< + CursorInputMapper>(deviceContext, mReaderConfiguration, + CursorInputMapperUnitTest::isPointerChoreographerEnabled()); + + // The mapper should not generate any events because it is associated with a display that is + // different from the pointer display. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, testing::IsEmpty()); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtons) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + // press BTN_LEFT, release BTN_LEFT + args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))))); + args.clear(); + + // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE + args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1); + args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), + WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(100.0f, 200.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(100.0f, 200.0f), WithPressure(0.0f))))); +} + +class CursorInputMapperButtonKeyTest + : public CursorInputMapperUnitTest, + public testing::WithParamInterface< + std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/, + int32_t /*expectedKeyCode*/>> { + virtual bool isPointerChoreographerEnabled() override { return false; } +}; + +TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKey) { + auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam(); + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), + WithKeyCode(expectedKeyCode))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(expectedButtonState), + WithCoords(100.0f, 200.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(expectedButtonState), + WithCoords(100.0f, 200.0f), WithPressure(0.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), + WithKeyCode(expectedKeyCode))))); +} + +INSTANTIATE_TEST_SUITE_P( + SideExtraBackAndForward, CursorInputMapperButtonKeyTest, + testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK), + std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD), + std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK), + std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD, + AKEYCODE_FORWARD))); + +TEST_F(CursorInputMapperUnitTest, ProcessShouldMoveThePointerAroundInPointerMode) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(110.0f, 220.0f), WithPressure(0.0f), WithSize(0.0f), + WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f), + WithOrientation(0.0f), WithDistance(0.0f))))); + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); +} + +/** + * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any + * pointer acceleration or speed processing should not be applied. + */ +TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, + /*highThreshold=*/100.f, /*acceleration=*/10.f); + mReaderConfiguration.pointerVelocityControlParameters = testParams; + mFakePolicy->setVelocityControlParams(testParams); + createMapper(); + + std::list<NotifyArgs> args; + + // Move and verify scale is applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))))); + NotifyMotionArgs motionArgs = std::get<NotifyMotionArgs>(args.front()); + const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_GT(relX, 10); + ASSERT_GT(relY, 20); + args.clear(); + + // Enable Pointer Capture + setPointerCapture(true); + + // Move and verify scale is not applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), + WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(10, 20))))); +} + +// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging +// logic can be removed. +class CursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + input_flags::enable_new_mouse_pointer_ballistics(false); + CursorInputMapperUnitTestBase::SetUp(); + } + + bool isPointerChoreographerEnabled() override { return true; } +}; + +TEST_F(CursorInputMapperUnitTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + mFakePolicy->clearViewports(); + mFakePointerController->clearBounds(); + createMapper(); + + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + // Initially there should not be a valid motion range because there's no viewport or pointer + // bounds. + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); + + // When the viewport and the default pointer display ID is set, then there should be a valid + // motion range. + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); + std::list<NotifyArgs> args = + mMapper->reconfigure(systemTime(), mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_THAT(args, testing::IsEmpty()); + + InputDeviceInfo info2; + mMapper->populateDeviceInfo(info2); + + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0, + DISPLAY_WIDTH - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0, + DISPLAY_HEIGHT - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the secondary display as the display on which the pointer should be shown. + // The InputDevice is not associated with any display. + mFakePointerController->setDisplayViewport(secondaryViewport); + mFakePointerController->setPosition(100, 200); + createDevice(); + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + // Ensure input events are generated for the secondary display. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(0.0f, 0.0f))))); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, + ConfigureDisplayIdShouldGenerateEventForMismatchedPointerDisplay) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the primary display as the display on which the pointer should be shown. + mFakePointerController->setDisplayViewport(primaryViewport); + createDevice(); + // Associate the InputDevice with the secondary display. + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + // With PointerChoreographer enabled, there could be a PointerController for the associated + // display even if it is different from the pointer display. So the mapper should generate an + // event. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(0.0f, 0.0f))))); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + // press BTN_LEFT, release BTN_LEFT + args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + args.clear(); + args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))))); + args.clear(); + + // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE + args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1); + args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), + WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); +} + +class CursorInputMapperButtonKeyTestWithChoreographer + : public CursorInputMapperUnitTestWithChoreographer, + public testing::WithParamInterface< + std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/, + int32_t /*expectedKeyCode*/>> {}; + +TEST_P(CursorInputMapperButtonKeyTestWithChoreographer, + ProcessShouldHandleButtonKeyWithZeroCoords) { + auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam(); + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), + WithKeyCode(expectedKeyCode))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(expectedButtonState), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(expectedButtonState), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), + WithKeyCode(expectedKeyCode))))); +} + +INSTANTIATE_TEST_SUITE_P( + SideExtraBackAndForward, CursorInputMapperButtonKeyTestWithChoreographer, + testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK), + std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD), + std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK), + std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD, + AKEYCODE_FORWARD))); + +TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f), WithSize(0.0f), + WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f), + WithOrientation(0.0f), WithDistance(0.0f))))); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, + /*highThreshold=*/100.f, /*acceleration=*/10.f); + mReaderConfiguration.pointerVelocityControlParameters = testParams; + mFakePolicy->setVelocityControlParams(testParams); + createMapper(); + + NotifyMotionArgs motionArgs; + std::list<NotifyArgs> args; + + // Move and verify scale is applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))))); + motionArgs = std::get<NotifyMotionArgs>(args.front()); + const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_GT(relX, 10); + ASSERT_GT(relY, 20); + args.clear(); + + // Enable Pointer Capture + setPointerCapture(true); + + // Move and verify scale is not applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), + WithMotionAction(AMOTION_EVENT_ACTION_MOVE))))); + motionArgs = std::get<NotifyMotionArgs>(args.front()); + const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_EQ(10, relX2); + ASSERT_EQ(20, relY2); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) { + // Set up the default display. + mFakePolicy->clearViewports(); + mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); + + // Set up the secondary display as the display on which the pointer should be shown. + // The InputDevice is not associated with any display. + mFakePolicy->addDisplayViewport(createSecondaryViewport()); + mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); + + createMapper(); + + mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); + mFakePointerController->setPosition(100, 200); + + // Ensure input events are generated without display ID or coords, because they will be decided + // later by PointerChoreographer. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE), + WithCoords(0.0f, 0.0f))))); +} + +// TODO(b/320433834): De-duplicate the test cases once the flag is removed. +class CursorInputMapperUnitTestWithNewBallistics : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + input_flags::enable_new_mouse_pointer_ballistics(true); + CursorInputMapperUnitTestBase::SetUp(); + } + + bool isPointerChoreographerEnabled() override { return true; } +}; + +TEST_F(CursorInputMapperUnitTestWithNewBallistics, PointerCaptureDisablesVelocityProcessing) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + NotifyMotionArgs motionArgs; + std::list<NotifyArgs> args; + + // Move and verify scale is applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + motionArgs = std::get<NotifyMotionArgs>(args.front()); + const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_GT(relX, 10); + ASSERT_GT(relY, 20); + args.clear(); + + // Enable Pointer Capture + setPointerCapture(true); + + // Move and verify scale is not applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + motionArgs = std::get<NotifyMotionArgs>(args.front()); + const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_EQ(10, relX2); + ASSERT_EQ(20, relY2); +} + +TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAssociatedViewport) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0); + mReaderConfiguration.setDisplayViewports({primaryViewport}); + createDevice(); + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, primaryViewport); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + + // Verify that acceleration is being applied by default by checking that the movement is scaled. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithDisplayId(DISPLAY_ID))))); + const auto& coords = get<NotifyMotionArgs>(args.back()).pointerCoords[0]; + ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), 10.f); + ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f); + + // Disable acceleration for the display, and verify that acceleration is no longer applied. + mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID); + args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::POINTER_SPEED); + args.clear(); + + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>(AllOf(WithMotionAction(HOVER_MOVE), + WithDisplayId(DISPLAY_ID), + WithRelativeMotion(10, 20))))); +} + +TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDisplayChange) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0); + mReaderConfiguration.setDisplayViewports({primaryViewport}); + // Disable acceleration for the display. + mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID); + createDevice(); + + // Don't associate the device with the display yet. + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, + /*viewport=*/std::nullopt); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + + // Verify that acceleration is being applied by default by checking that the movement is scaled. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + const auto& coords = get<NotifyMotionArgs>(args.back()).pointerCoords[0]; + ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), 10.f); + ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f); + + // Now associate the device with the display, and verify that acceleration is disabled. + deviceContext.setViewport(primaryViewport); + args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + args.clear(); + + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithDisplayId(DISPLAY_ID), + WithRelativeMotion(10, 20))))); +} + +namespace { + +// Minimum timestamp separation between subsequent input events from a Bluetooth device. +constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); +// Maximum smoothing time delta so that we don't generate events too far into the future. +constexpr nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); + +} // namespace + +class BluetoothCursorInputMapperUnitTest : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + SetUpWithBus(BUS_BLUETOOTH); + + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + } +}; + +TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + } +} + +TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningIsCapped) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // Process several events with the same timestamp from the kernel. + // Ensure that we do not generate events too far into the future. + constexpr static int32_t numEvents = + MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; + for (int i = 0; i < numEvents; i++) { + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + } + + // By processing more events with the same timestamp, we should not generate events with a + // timestamp that is more than the specified max time delta from the timestamp at its injection. + const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; + for (int i = 0; i < 3; i++) { + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(cappedEventTime))))); + argsList.clear(); + } +} + +TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningNotUsed) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp + // smoothening is not needed, its timestamp is not affected. + kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); + expectedEventTime = kernelEventTime; + + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); +} + +// --- BluetoothCursorInputMapperUnitTestWithChoreographer --- + +class BluetoothCursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + SetUpWithBus(BUS_BLUETOOTH); + + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + } + + bool isPointerChoreographerEnabled() override { return true; } +}; + +TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmoothening) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + } +} + +TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningIsCapped) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // Process several events with the same timestamp from the kernel. + // Ensure that we do not generate events too far into the future. + constexpr static int32_t numEvents = + MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; + for (int i = 0; i < numEvents; i++) { + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + } + + // By processing more events with the same timestamp, we should not generate events with a + // timestamp that is more than the specified max time delta from the timestamp at its injection. + const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; + for (int i = 0; i < 3; i++) { + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(cappedEventTime))))); + argsList.clear(); + } +} + +TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningNotUsed) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp + // smoothening is not needed, its timestamp is not affected. + kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); + expectedEventTime = kernelEventTime; + + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); +} + } // namespace android diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp index 212fcebecd..daa000f2ce 100644 --- a/services/inputflinger/tests/FakeEventHub.cpp +++ b/services/inputflinger/tests/FakeEventHub.cpp @@ -431,6 +431,38 @@ status_t FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, return -1; } +void FakeEventHub::setMtSlotValues(int32_t deviceId, int32_t axis, + const std::vector<int32_t>& values) { + Device* device = getDevice(deviceId); + if (!device) { + FAIL() << "Missing device"; + } + device->mtSlotValues[axis] = values; +} + +base::Result<std::vector<int32_t>> FakeEventHub::getMtSlotValues(int32_t deviceId, int32_t axis, + size_t slotCount) const { + Device* device = getDevice(deviceId); + if (!device) { + ADD_FAILURE() << "Missing device"; + return base::ResultError("Missing device", UNKNOWN_ERROR); + } + const auto& mtSlotValuesIterator = device->mtSlotValues.find(axis); + if (mtSlotValuesIterator == device->mtSlotValues.end()) { + return base::ResultError("axis not supported", NAME_NOT_FOUND); + } + const auto& mtSlotValues = mtSlotValuesIterator->second; + if (mtSlotValues.size() != slotCount) { + ADD_FAILURE() << "MtSlot values specified for " << mtSlotValues.size() + << " slots but expected for " << slotCount << " Slots"; + return base::ResultError("Slot count mismatch", NAME_NOT_FOUND); + } + std::vector<int32_t> outValues(slotCount + 1); + outValues[0] = axis; + std::copy(mtSlotValues.begin(), mtSlotValues.end(), outValues.begin() + 1); + return std::move(outValues); +} + int32_t FakeEventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { Device* device = getDevice(deviceId); if (!device) { diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h index 8e06940aec..f07b3441c2 100644 --- a/services/inputflinger/tests/FakeEventHub.h +++ b/services/inputflinger/tests/FakeEventHub.h @@ -65,6 +65,7 @@ class FakeEventHub : public EventHubInterface { bool enabled; std::optional<RawLayoutInfo> layoutInfo; std::string sysfsRootPath; + std::unordered_map<int32_t, std::vector<int32_t>> mtSlotValues; status_t enable() { enabled = true; @@ -154,6 +155,11 @@ public: int32_t value); void assertQueueIsEmpty(); void setSysfsRootPath(int32_t deviceId, std::string sysfsRootPath) const; + // Populate fake slot values to be returned by the getter, size of the values should be equal to + // the slot count + void setMtSlotValues(int32_t deviceId, int32_t axis, const std::vector<int32_t>& values); + base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis, + size_t slotCount) const override; private: Device* getDevice(int32_t deviceId) const; diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index e9d93af67b..fb2db06aff 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -63,7 +63,7 @@ private: void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {} - void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} + void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {} nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override { return 0; diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 88f514f1d4..9e9371248e 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -41,15 +41,21 @@ void FakeInputReaderPolicy::assertInputDevicesNotChanged() { } void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mStylusGestureNotified); - ASSERT_EQ(deviceId, *mStylusGestureNotified); - mStylusGestureNotified.reset(); + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + const bool success = + mStylusGestureNotifiedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mDeviceIdOfNotifiedStylusGesture.has_value(); + }); + ASSERT_TRUE(success) << "Timed out waiting for stylus gesture to be notified"; + ASSERT_EQ(deviceId, *mDeviceIdOfNotifiedStylusGesture); + mDeviceIdOfNotifiedStylusGesture.reset(); } void FakeInputReaderPolicy::assertStylusGestureNotNotified() { std::scoped_lock lock(mLock); - ASSERT_FALSE(mStylusGestureNotified); + ASSERT_FALSE(mDeviceIdOfNotifiedStylusGesture); } void FakeInputReaderPolicy::clearViewports() { @@ -258,7 +264,8 @@ void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> proces void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) { std::scoped_lock lock(mLock); - mStylusGestureNotified = deviceId; + mDeviceIdOfNotifiedStylusGesture = deviceId; + mStylusGestureNotifiedCondition.notify_all(); } std::optional<DisplayViewport> FakeInputReaderPolicy::getPointerViewportForAssociatedDisplay( diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 4ef9c3ee4a..da5085db7c 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -102,9 +102,11 @@ private: bool mInputDevicesChanged GUARDED_BY(mLock){false}; std::vector<DisplayViewport> mViewports; TouchAffineTransformation transform; - std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){}; bool mIsInputMethodConnectionActive{false}; + std::condition_variable mStylusGestureNotifiedCondition; + std::optional<DeviceId> mDeviceIdOfNotifiedStylusGesture GUARDED_BY(mLock){}; + uint32_t mNextPointerCaptureSequenceNumber{0}; }; diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp new file mode 100644 index 0000000000..4655ee8458 --- /dev/null +++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp @@ -0,0 +1,190 @@ +/* + * 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 "FakeInputTracingBackend.h" + +#include <android-base/logging.h> +#include <utils/Errors.h> + +namespace android::inputdispatcher { + +namespace { + +// Use a larger timeout while waiting for events to be traced, compared to the timeout used while +// waiting to receive events through the input channel. Events are traced from a separate thread, +// which does not have the same high thread priority as the InputDispatcher's thread, so the tracer +// is expected to lag behind the Dispatcher at times. +constexpr auto TRACE_TIMEOUT = std::chrono::seconds(5); + +base::ResultError<> error(const std::ostringstream& ss) { + return base::ResultError(ss.str(), BAD_VALUE); +} + +inline auto getId(const trace::TracedEvent& v) { + return std::visit([](const auto& event) { return event.id; }, v); +} + +MotionEvent toInputEvent( + const trace::TracedMotionEvent& e, + const trace::InputTracingBackendInterface::WindowDispatchArgs& dispatchArgs, + const std::array<uint8_t, 32>& hmac) { + MotionEvent traced; + traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action, e.actionButton, + dispatchArgs.resolvedFlags, e.edgeFlags, e.metaState, e.buttonState, + e.classification, dispatchArgs.transform, e.xPrecision, e.yPrecision, + e.xCursorPosition, e.yCursorPosition, dispatchArgs.rawTransform, e.downTime, + e.eventTime, e.pointerProperties.size(), e.pointerProperties.data(), + e.pointerCoords.data()); + return traced; +} + +KeyEvent toInputEvent(const trace::TracedKeyEvent& e, + const trace::InputTracingBackendInterface::WindowDispatchArgs& dispatchArgs, + const std::array<uint8_t, 32>& hmac) { + KeyEvent traced; + traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action, + dispatchArgs.resolvedFlags, e.keyCode, e.scanCode, e.metaState, e.repeatCount, + e.downTime, e.eventTime); + return traced; +} + +} // namespace + +// --- VerifyingTrace --- + +void VerifyingTrace::expectKeyDispatchTraced(const KeyEvent& event, int32_t windowId) { + std::scoped_lock lock(mLock); + mExpectedEvents.emplace_back(event, windowId); +} + +void VerifyingTrace::expectMotionDispatchTraced(const MotionEvent& event, int32_t windowId) { + std::scoped_lock lock(mLock); + mExpectedEvents.emplace_back(event, windowId); +} + +void VerifyingTrace::verifyExpectedEventsTraced() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + // Poll for all expected events to be traced, and keep track of the latest poll result. + base::Result<void> result; + mEventTracedCondition.wait_for(lock, TRACE_TIMEOUT, [&]() REQUIRES(mLock) { + for (const auto& [expectedEvent, windowId] : mExpectedEvents) { + std::visit([&](const auto& event) + REQUIRES(mLock) { result = verifyEventTraced(event, windowId); }, + expectedEvent); + if (!result.ok()) { + return false; + } + } + return true; + }); + + EXPECT_TRUE(result.ok()) + << "Timed out waiting for all expected events to be traced successfully: " + << result.error().message(); +} + +void VerifyingTrace::reset() { + std::scoped_lock lock(mLock); + mTracedEvents.clear(); + mTracedWindowDispatches.clear(); + mExpectedEvents.clear(); +} + +template <typename Event> +base::Result<void> VerifyingTrace::verifyEventTraced(const Event& expectedEvent, + int32_t expectedWindowId) const { + std::ostringstream msg; + + auto tracedEventsIt = mTracedEvents.find(expectedEvent.getId()); + if (tracedEventsIt == mTracedEvents.end()) { + msg << "Expected event with ID 0x" << std::hex << expectedEvent.getId() + << " to be traced, but it was not.\n" + << "Expected event: " << expectedEvent; + return error(msg); + } + + auto tracedDispatchesIt = + std::find_if(mTracedWindowDispatches.begin(), mTracedWindowDispatches.end(), + [&](const WindowDispatchArgs& args) { + return args.windowId == expectedWindowId && + getId(args.eventEntry) == expectedEvent.getId(); + }); + if (tracedDispatchesIt == mTracedWindowDispatches.end()) { + msg << "Expected dispatch of event with ID 0x" << std::hex << expectedEvent.getId() + << " to window with ID 0x" << expectedWindowId << " to be traced, but it was not.\n" + << "Expected event: " << expectedEvent; + return error(msg); + } + + // Verify that the traced event matches the expected event exactly. + return std::visit( + [&](const auto& traced) -> base::Result<void> { + Event tracedEvent; + using T = std::decay_t<decltype(traced)>; + if constexpr (std::is_same_v<Event, MotionEvent> && + std::is_same_v<T, trace::TracedMotionEvent>) { + tracedEvent = + toInputEvent(traced, *tracedDispatchesIt, expectedEvent.getHmac()); + } else if constexpr (std::is_same_v<Event, KeyEvent> && + std::is_same_v<T, trace::TracedKeyEvent>) { + tracedEvent = + toInputEvent(traced, *tracedDispatchesIt, expectedEvent.getHmac()); + } else { + msg << "Received the wrong event type!\n" + << "Expected event: " << expectedEvent; + return error(msg); + } + + const auto result = testing::internal::CmpHelperEQ("expectedEvent", "tracedEvent", + expectedEvent, tracedEvent); + if (!result) { + msg << result.failure_message(); + return error(msg); + } + return {}; + }, + tracedEventsIt->second); +} + +// --- FakeInputTracingBackend --- + +void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) { + { + std::scoped_lock lock(mTrace->mLock); + mTrace->mTracedEvents.emplace(event.id, event); + } + mTrace->mEventTracedCondition.notify_all(); +} + +void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) { + { + std::scoped_lock lock(mTrace->mLock); + mTrace->mTracedEvents.emplace(event.id, event); + } + mTrace->mEventTracedCondition.notify_all(); +} + +void FakeInputTracingBackend::traceWindowDispatch(const WindowDispatchArgs& args) { + { + std::scoped_lock lock(mTrace->mLock); + mTrace->mTracedWindowDispatches.push_back(args); + } + mTrace->mEventTracedCondition.notify_all(); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h new file mode 100644 index 0000000000..1b3613d5c5 --- /dev/null +++ b/services/inputflinger/tests/FakeInputTracingBackend.h @@ -0,0 +1,91 @@ +/* + * 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 "../dispatcher/trace/InputTracingBackendInterface.h" + +#include <android-base/result.h> +#include <android-base/thread_annotations.h> +#include <gtest/gtest.h> +#include <input/Input.h> +#include <condition_variable> +#include <memory> +#include <mutex> +#include <unordered_map> +#include <vector> + +namespace android::inputdispatcher { + +/** + * A class representing an input trace, used to make assertions on what was traced by + * InputDispatcher in tests. This class is thread-safe. + */ +class VerifyingTrace { +public: + VerifyingTrace() = default; + + /** Add an expectation for a key event to be traced. */ + void expectKeyDispatchTraced(const KeyEvent& event, int32_t windowId); + + /** Add an expectation for a motion event to be traced. */ + void expectMotionDispatchTraced(const MotionEvent& event, int32_t windowId); + + /** + * Wait and verify that all expected events are traced. + * This is a lenient verifier that does not expect the events to be traced in the order + * that the events were expected, and does not fail if there are events that are traced that + * were not expected. Verifying does not clear the expectations. + */ + void verifyExpectedEventsTraced(); + + /** Reset the trace and clear all expectations. */ + void reset(); + +private: + std::mutex mLock; + std::condition_variable mEventTracedCondition; + std::unordered_map<uint32_t /*eventId*/, trace::TracedEvent> mTracedEvents GUARDED_BY(mLock); + using WindowDispatchArgs = trace::InputTracingBackendInterface::WindowDispatchArgs; + std::vector<WindowDispatchArgs> mTracedWindowDispatches GUARDED_BY(mLock); + std::vector<std::pair<std::variant<KeyEvent, MotionEvent>, int32_t /*windowId*/>> + mExpectedEvents GUARDED_BY(mLock); + + friend class FakeInputTracingBackend; + + // Helper to verify that the given event appears as expected in the trace. If the verification + // fails, the error message describes why. + template <typename Event> + base::Result<void> verifyEventTraced(const Event&, int32_t windowId) const REQUIRES(mLock); +}; + +/** + * A backend implementation for input tracing that records events to the provided + * VerifyingTrace used for testing. + */ +class FakeInputTracingBackend : public trace::InputTracingBackendInterface { +public: + FakeInputTracingBackend(std::shared_ptr<VerifyingTrace> trace) : mTrace(trace) {} + +private: + std::shared_ptr<VerifyingTrace> mTrace; + + void traceKeyEvent(const trace::TracedKeyEvent& entry) override; + void traceMotionEvent(const trace::TracedMotionEvent& entry) override; + void traceWindowDispatch(const WindowDispatchArgs& entry) override; +}; + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp index 80319f2487..dc199e2729 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -28,21 +28,31 @@ void FakePointerController::setBounds(float minX, float minY, float maxX, float mMaxY = maxY; } +void FakePointerController::clearBounds() { + mHaveBounds = false; +} + const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() { return mSpotsByDisplay; } void FakePointerController::setPosition(float x, float y) { + if (!mEnabled) return; + mX = x; mY = y; } FloatPoint FakePointerController::getPosition() const { + if (!mEnabled) { + return {0, 0}; + } + return {mX, mY}; } int32_t FakePointerController::getDisplayId() const { - if (!mDisplayId) { + if (!mEnabled || !mDisplayId) { return ADISPLAY_ID_NONE; } return *mDisplayId; @@ -60,6 +70,8 @@ void FakePointerController::updatePointerIcon(PointerIconStyle iconId) { } void FakePointerController::setCustomPointerIcon(const SpriteIcon& icon) { + if (!mEnabled) return; + ASSERT_FALSE(mCustomIconStyle.has_value()) << "Custom pointer icon was set more than once"; mCustomIconStyle = icon.style; } @@ -110,10 +122,14 @@ bool FakePointerController::isPointerShown() { } std::optional<FloatRect> FakePointerController::getBounds() const { + if (!mEnabled) return std::nullopt; + return mHaveBounds ? std::make_optional<FloatRect>(mMinX, mMinY, mMaxX, mMaxY) : std::nullopt; } void FakePointerController::move(float deltaX, float deltaY) { + if (!mEnabled) return; + mX += deltaX; if (mX < mMinX) mX = mMinX; if (mX > mMaxX) mX = mMaxX; @@ -123,14 +139,20 @@ void FakePointerController::move(float deltaX, float deltaY) { } void FakePointerController::fade(Transition) { + if (!mEnabled) return; + mIsPointerShown = false; } void FakePointerController::unfade(Transition) { + if (!mEnabled) return; + mIsPointerShown = true; } void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits, int32_t displayId) { + if (!mEnabled) return; + std::vector<int32_t> newSpots; // Add spots for fingers that are down. for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { @@ -142,6 +164,8 @@ void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitS } void FakePointerController::clearSpots() { + if (!mEnabled) return; + mSpotsByDisplay.clear(); } diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index 800f8649fd..536b447215 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -30,9 +30,13 @@ struct SpriteIcon { class FakePointerController : public PointerControllerInterface { public: + FakePointerController() : FakePointerController(/*enabled=*/true) {} + FakePointerController(bool enabled) : mEnabled(enabled) {} + virtual ~FakePointerController() {} void setBounds(float minX, float minY, float maxX, float maxY); + void clearBounds(); const std::map<int32_t, std::vector<int32_t>>& getSpots(); void setPosition(float x, float y) override; @@ -63,6 +67,7 @@ private: int32_t displayId) override; void clearSpots() override; + const bool mEnabled; bool mHaveBounds{false}; float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0}; float mX{0}, mY{0}; diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index d2b68dd93e..337b52b677 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -41,10 +41,15 @@ namespace { const auto TOUCHPAD_PALM_REJECTION = ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection); +const auto TOUCHPAD_PALM_REJECTION_V2 = + ACONFIG_FLAG(input_flags, enable_v2_touchpad_typing_palm_rejection); } // namespace using testing::AllOf; +using testing::Each; +using testing::ElementsAre; +using testing::VariantWith; class GestureConverterTestBase : public testing::Test { protected: @@ -64,7 +69,8 @@ protected: mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, -500, 500, 0, 0, 20); mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, -500, 500, 0, 0, 20); - mFakePointerController = std::make_shared<FakePointerController>(); + mFakePointerController = std::make_shared<FakePointerController>( + /*enabled=*/!input_flags::enable_pointer_choreographer()); mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); mFakePointerController->setPosition(POINTER_X, POINTER_Y); mFakePolicy->setPointerController(mFakePointerController); @@ -107,16 +113,36 @@ TEST_F(GestureConverterTest, Move) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithRelativeMotion(-5, 10), WithButtonState(0), + WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); + + // The same gesture again should only repeat the HOVER_MOVE and cursor position change, not the + // HOVER_ENTER. + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X - 10, POINTER_Y + 20), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 10, POINTER_Y + 20)); } TEST_F(GestureConverterTest, Move_Rotated) { @@ -126,14 +152,21 @@ TEST_F(GestureConverterTest, Move_Rotated) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X + 10, POINTER_Y + 5), + WithRelativeMotion(10, 5), WithButtonState(0), + WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5)); } @@ -147,67 +180,78 @@ TEST_F(GestureConverterTest, ButtonsChange) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Then release the left button Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, leftUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Finally release the right button Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture); - ASSERT_EQ(3u, args.size()); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, rightUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY))), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), + WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); +} - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); +TEST_F(GestureConverterTest, ButtonDownAfterMoveExitsHover) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, + /*is_tap=*/false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args.front(), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)))); } TEST_F(GestureConverterTest, DragWithButton) { @@ -219,32 +263,31 @@ TEST_F(GestureConverterTest, DragWithButton) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Move Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), + WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); @@ -252,24 +295,20 @@ TEST_F(GestureConverterTest, DragWithButton) { Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, upGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll) { @@ -279,50 +318,55 @@ TEST_F(GestureConverterTest, Scroll) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDownTime(downTime), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X, POINTER_Y - 10), - WithGestureScrollDistance(0, 10, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithDownTime(downTime))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X, POINTER_Y - 10), + WithGestureScrollDistance(0, 10, EPSILON))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X, POINTER_Y - 15), - WithGestureScrollDistance(0, 5, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X, POINTER_Y - 15), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_Rotated) { @@ -333,43 +377,51 @@ TEST_F(GestureConverterTest, Scroll_Rotated) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDownTime(downTime), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X - 10, POINTER_Y), - WithGestureScrollDistance(0, 10, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithDownTime(downTime))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 10, POINTER_Y), + WithGestureScrollDistance(0, 10, EPSILON))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(POINTER_X - 15, POINTER_Y), - WithGestureScrollDistance(0, 5, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X - 15, POINTER_Y), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) { @@ -378,21 +430,22 @@ TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionClassification(MotionClassification::NONE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::NONE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) { @@ -401,20 +454,21 @@ TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like pinch. Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON)); } @@ -426,17 +480,18 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAfterGesture) Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, /*dy=*/0); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5, /*dy=*/10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionClassification(MotionClassification::NONE)); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionClassification(MotionClassification::NONE)))); } TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) { @@ -446,16 +501,17 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5, /*dy=*/5); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like pinch. Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0))); @@ -472,48 +528,42 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(4u, args.size()); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithGestureSwipeFingerCount(3), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Three fake fingers should be created. We don't actually care where they are, so long as they // move appropriately. NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithPointerCount(1u))); PointerCoords finger0Start = arg.pointerCoords[0]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))); PointerCoords finger1Start = arg.pointerCoords[1]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))); PointerCoords finger2Start = arg.pointerCoords[2]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithGestureOffset(0, -0.01, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u))); EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX()); EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX()); EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX()); @@ -523,7 +573,7 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -540,30 +590,40 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { @@ -574,39 +634,38 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(4u, args.size()); + ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT)))); // Three fake fingers should be created. We don't actually care where they are, so long as they // move appropriately. NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), - WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithPointerCount(1u))); PointerCoords finger0Start = arg.pointerCoords[0]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))); PointerCoords finger1Start = arg.pointerCoords[1]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))); PointerCoords finger2Start = arg.pointerCoords[2]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u))); EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10); EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10); EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10); @@ -616,7 +675,7 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -631,23 +690,24 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)))); + ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT)))); } TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { @@ -657,58 +717,49 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 10, /* dy= */ 0); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(5u, args.size()); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithGestureSwipeFingerCount(4), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Four fake fingers should be created. We don't actually care where they are, so long as they // move appropriately. NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithPointerCount(1u))); PointerCoords finger0Start = arg.pointerCoords[0]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))); PointerCoords finger1Start = arg.pointerCoords[1]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))); PointerCoords finger2Start = arg.pointerCoords[2]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(4u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(4u))); PointerCoords finger3Start = arg.pointerCoords[3]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithGestureOffset(0.01, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(4u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0.01, 0, EPSILON), WithPointerCount(4u))); EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10); EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10); EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10); @@ -720,7 +771,7 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 5, /* dy= */ 0); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -739,38 +790,49 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY()); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(4u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(4u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Pinch_Inwards) { @@ -780,51 +842,62 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), - WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X - 100, POINTER_Y), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerCoords(1, POINTER_X + 100, POINTER_Y), + WithPointerCount(2u))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 0.8, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(0.8f, EPSILON), - WithPointerCoords(0, POINTER_X - 80, POINTER_Y), - WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(0.8f, EPSILON), + WithPointerCoords(0, POINTER_X - 80, POINTER_Y), + WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Pinch_Outwards) { @@ -834,51 +907,62 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), - WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(POINTER_X - 100, POINTER_Y), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerCoords(1, POINTER_X + 100, POINTER_Y), + WithPointerCount(2u))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.2f, EPSILON), - WithPointerCoords(0, POINTER_X - 120, POINTER_Y), - WithPointerCoords(1, POINTER_X + 120, POINTER_Y), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.2f, EPSILON), + WithPointerCoords(0, POINTER_X - 120, POINTER_Y), + WithPointerCoords(1, POINTER_X + 120, POINTER_Y), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) { @@ -888,21 +972,22 @@ TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionClassification(MotionClassification::NONE)); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionClassification(MotionClassification::NONE)))); } TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) { @@ -912,21 +997,22 @@ TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like scroll. Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1, /*dy=*/0); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, scrollGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON)); } @@ -939,28 +1025,28 @@ TEST_F(GestureConverterTest, ResetWithButtonPressed) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(3u, args.size()); - - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ResetDuringScroll) { @@ -969,18 +1055,24 @@ TEST_F(GestureConverterTest, ResetDuringScroll) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X, POINTER_Y - 10), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) { @@ -990,31 +1082,38 @@ TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, /*dy=*/10); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(3u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ResetDuringPinch) { @@ -1024,22 +1123,29 @@ TEST_F(GestureConverterTest, ResetDuringPinch) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, FlingTapDown) { @@ -1049,10 +1155,11 @@ TEST_F(GestureConverterTest, FlingTapDown) { Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); @@ -1061,6 +1168,38 @@ TEST_F(GestureConverterTest, FlingTapDown) { ASSERT_TRUE(mFakePointerController->isPointerShown()); } +TEST_F(GestureConverterTest, FlingTapDownAfterScrollStopsFling) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + input_flags::enable_touchpad_fling_stop(true); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture); + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, + GESTURES_FLING_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + + Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); +} + TEST_F(GestureConverterTest, Tap) { // Tap should produce button press/release events InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); @@ -1069,52 +1208,43 @@ TEST_F(GestureConverterTest, Tap) { Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); - - ASSERT_EQ(5u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0), WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Click) { @@ -1125,62 +1255,61 @@ TEST_F(GestureConverterTest, Click) { Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); - - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); - - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION), + REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) { + nsecs_t currentTime = ARBITRARY_GESTURE_TIME; + // Tap should be ignored when disabled mReader->getContext()->setPreventingTouchpadTaps(true); @@ -1188,28 +1317,106 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled, GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ADISPLAY_ID_DEFAULT); - Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); + Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); - Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + // no events should be generated + ASSERT_EQ(0u, args.size()); + + // Future taps should be re-enabled + ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); +} + +TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) { + nsecs_t currentTime = ARBITRARY_GESTURE_TIME; + + // Tap should be ignored when disabled + mReader->getContext()->setPreventingTouchpadTaps(true); + + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> args = + converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. + + Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); // no events should be generated ASSERT_EQ(0u, args.size()); // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); + + // taps before the threshold should still be ignored + currentTime += TAP_ENABLE_DELAY_NANOS.count(); + flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0))); + + tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); + + // no events should be generated + ASSERT_EQ(0u, args.size()); + + // taps after the threshold should be recognised + currentTime += 1; + flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0))); + + tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0))))); + ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithRelativeMotion(0.f, 0.f)))); } TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, @@ -1223,58 +1430,54 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); - - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0), WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); @@ -1290,21 +1493,57 @@ TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick, converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + // We don't need to check args here, since it's covered by the Move test. // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); } +TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) { + const nsecs_t gestureStartTime = 1000; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + // Start a move gesture at gestureStartTime + Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + + // Key presses with IME connection should cancel ongoing move gesture + nsecs_t currentTime = gestureStartTime + 100; + mFakePolicy->setIsInputMethodConnectionActive(true); + mReader->getContext()->setLastKeyDownTimestamp(currentTime); + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)))); + + // any updates in existing move gesture should be ignored + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_EQ(0u, args.size()); + + // New gesture should not be affected + currentTime += 100; + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); +} + // TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging // logic can be removed. class GestureConverterTestWithChoreographer : public GestureConverterTestBase { @@ -1321,13 +1560,29 @@ TEST_F(GestureConverterTestWithChoreographer, Move) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithRelativeMotion(0, 0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithRelativeMotion(-5, 10), WithButtonState(0), + WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + // The same gesture again should only repeat the HOVER_MOVE, not the HOVER_ENTER. + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) { @@ -1337,13 +1592,20 @@ TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithRelativeMotion(0, 0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithRelativeMotion(10, 5), WithButtonState(0), + WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { @@ -1355,65 +1617,77 @@ TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | - AMOTION_EVENT_BUTTON_SECONDARY), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Then release the left button Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, leftUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Finally release the right button Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture); - ASSERT_EQ(3u, args.size()); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, rightUpGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY))), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); +} - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); +TEST_F(GestureConverterTestWithChoreographer, ButtonDownAfterMoveExitsHover) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, + /*is_tap=*/false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args.front(), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT)))); } TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { @@ -1425,53 +1699,48 @@ TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Move Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0), - WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Release the button Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture); - ASSERT_EQ(3u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, upGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_UP)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll) { @@ -1481,47 +1750,54 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDownTime(downTime), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -10), - WithGestureScrollDistance(0, 10, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithDownTime(downTime))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(0, -10), + WithGestureScrollDistance(0, 10, EPSILON))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -15), - WithGestureScrollDistance(0, 5, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -15), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0 - 15), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, -15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { @@ -1532,40 +1808,51 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDownTime(downTime), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-10, 0), - WithGestureScrollDistance(0, 10, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithDownTime(downTime))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(-10, 0), + WithGestureScrollDistance(0, 10, EPSILON))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-15, 0), - WithGestureScrollDistance(0, 5, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-15, 0), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(-15, 0), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(-15, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGesture) { @@ -1574,21 +1861,22 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGe converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionClassification(MotionClassification::NONE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::NONE), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsScrollDistanceAfterGesture) { @@ -1597,20 +1885,21 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsScrollDistanceAfterGe converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, GESTURES_FLING_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like pinch. Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON)); } @@ -1622,17 +1911,18 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsClassificat Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, /*dy=*/0); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5, /*dy=*/10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionClassification(MotionClassification::NONE)); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionClassification(MotionClassification::NONE)))); } TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) { @@ -1642,16 +1932,17 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsGestureAxes Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5, /*dy=*/5); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like pinch. Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0))); @@ -1668,48 +1959,42 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(4u, args.size()); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithGestureSwipeFingerCount(3), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Three fake fingers should be created. We don't actually care where they are, so long as they // move appropriately. NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithPointerCount(1u))); PointerCoords finger0Start = arg.pointerCoords[0]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))); PointerCoords finger1Start = arg.pointerCoords[1]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))); PointerCoords finger2Start = arg.pointerCoords[2]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithGestureOffset(0, -0.01, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u))); EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX()); EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX()); EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX()); @@ -1719,7 +2004,7 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -1736,30 +2021,40 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(3), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { @@ -1770,39 +2065,38 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(4u, args.size()); + ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT)))); // Three fake fingers should be created. We don't actually care where they are, so long as they // move appropriately. NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), - WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithPointerCount(1u))); PointerCoords finger0Start = arg.pointerCoords[0]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))); PointerCoords finger1Start = arg.pointerCoords[1]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))); PointerCoords finger2Start = arg.pointerCoords[2]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u))); EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10); EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10); EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10); @@ -1812,7 +2106,7 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, /* dy= */ 5); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -1827,23 +2121,24 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))))); + ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT)))); } TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { @@ -1853,58 +2148,49 @@ TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 10, /* dy= */ 0); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); ASSERT_EQ(5u, args.size()); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithGestureSwipeFingerCount(4), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Four fake fingers should be created. We don't actually care where they are, so long as they // move appropriately. NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithPointerCount(1u))); PointerCoords finger0Start = arg.pointerCoords[0]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))); PointerCoords finger1Start = arg.pointerCoords[1]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))); PointerCoords finger2Start = arg.pointerCoords[2]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(4u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0, 0, EPSILON), WithPointerCount(4u))); PointerCoords finger3Start = arg.pointerCoords[3]; args.pop_front(); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithGestureOffset(0.01, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(4u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + WithGestureOffset(0.01, 0, EPSILON), WithPointerCount(4u))); EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10); EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10); EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10); @@ -1916,7 +2202,7 @@ TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 5, /* dy= */ 0); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture); ASSERT_EQ(1u, args.size()); arg = std::get<NotifyMotionArgs>(args.front()); ASSERT_THAT(arg, @@ -1935,38 +2221,49 @@ TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY()); Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); - ASSERT_EQ(4u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(4u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithGestureSwipeFingerCount(4), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) { @@ -1976,50 +2273,59 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithCoords(-100, 0), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCoords(1, 100, 0), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(-100, 0), WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerCoords(1, 100, 0), WithPointerCount(2u))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 0.8, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(0.8f, EPSILON), WithPointerCoords(0, -80, 0), - WithPointerCoords(1, 80, 0), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(0.8f, EPSILON), + WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) { @@ -2029,50 +2335,59 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithCoords(-100, 0), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCoords(1, 100, 0), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(-100, 0), WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithPointerCoords(1, 100, 0), WithPointerCount(2u))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1.1, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.1f, EPSILON), WithPointerCoords(0, -110, 0), - WithPointerCoords(1, 110, 0), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.1f, EPSILON), + WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsClassificationAfterGesture) { @@ -2082,21 +2397,22 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsClassificationAfterGes Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - WithMotionClassification(MotionClassification::NONE)); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionClassification(MotionClassification::NONE)))); } TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsScaleFactorAfterGesture) { @@ -2106,21 +2422,22 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsScaleFactorAfterGestur Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1.2, GESTURES_ZOOM_UPDATE); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_END); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture); // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we // need to use another gesture type, like scroll. Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1, /*dy=*/0); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, scrollGesture); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture); ASSERT_FALSE(args.empty()); EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON)); } @@ -2133,27 +2450,28 @@ TEST_F(GestureConverterTestWithChoreographer, ResetWithButtonPressed) { Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(3u, args.size()); - - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), - WithCoords(0, 0), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) { @@ -2162,17 +2480,24 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) { converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, -10), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, -10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { @@ -2182,31 +2507,38 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, /*dy=*/10); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(3u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(3u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), - WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), - WithPointerCount(1u), WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) { @@ -2216,22 +2548,29 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) { Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, GESTURES_ZOOM_START); - (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture); std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); - ASSERT_EQ(2u, args.size()); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | - 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMotionClassification(MotionClassification::PINCH), - WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), - WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(2u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), + WithPointerCount(1u))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) { @@ -2241,14 +2580,47 @@ TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) { Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); } +TEST_F(GestureConverterTestWithChoreographer, FlingTapDownAfterScrollStopsFling) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + input_flags::enable_touchpad_fling_stop(true); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture); + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, + GESTURES_FLING_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + + Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithMotionClassification(MotionClassification::NONE))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); +} + TEST_F(GestureConverterTestWithChoreographer, Tap) { // Tap should produce button press/release events InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); @@ -2257,50 +2629,43 @@ TEST_F(GestureConverterTestWithChoreographer, Tap) { Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); - - ASSERT_EQ(5u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0), WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Click) { @@ -2311,60 +2676,62 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); - - ASSERT_EQ(2u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); - - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithPressure(0.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabled, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION), + REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) { + nsecs_t currentTime = ARBITRARY_GESTURE_TIME; + // Tap should be ignored when disabled mReader->getContext()->setPreventingTouchpadTaps(true); @@ -2372,27 +2739,106 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ADISPLAY_ID_DEFAULT); - Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + std::list<NotifyArgs> args = + converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); + Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); - Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + // no events should be generated + ASSERT_EQ(0u, args.size()); + + // Future taps should be re-enabled + ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); +} + +TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabledWithDelay, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) { + nsecs_t currentTime = ARBITRARY_GESTURE_TIME; + + // Tap should be ignored when disabled + mReader->getContext()->setPreventingTouchpadTaps(true); + + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> args = + converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. + + Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); // no events should be generated ASSERT_EQ(0u, args.size()); // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); + + // taps before the threshold should still be ignored + currentTime += TAP_ENABLE_DELAY_NANOS.count(); + flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0))); + + tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); + + // no events should be generated + ASSERT_EQ(0u, args.size()); + + // taps after the threshold should be recognised + currentTime += 1; + flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); + + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0))); + + tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0))))); + ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithRelativeMotion(0.f, 0.f)))); } TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisabled, @@ -2406,56 +2852,58 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisa Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); - ASSERT_EQ(2u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(0), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPressure(1.0f))))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); - args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); - - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), - WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), - WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); - args.pop_front(); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture); + + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(0), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); @@ -2471,16 +2919,55 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, MoveEnablesTapToClick, converter.setDisplayId(ADISPLAY_ID_DEFAULT); Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); - std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); - ASSERT_EQ(1u, args.size()); - - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + // We don't need to check args here, since it's covered by the Move test. // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); } +TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, KeypressCancelsHoverMove, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) { + const nsecs_t gestureStartTime = 1000; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + // Start a move gesture at gestureStartTime + Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + + // Key presses with IME connection should cancel ongoing move gesture + nsecs_t currentTime = gestureStartTime + 100; + mFakePolicy->setIsInputMethodConnectionActive(true); + mReader->getContext()->setLastKeyDownTimestamp(currentTime); + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)))); + + // any updates in existing move gesture should be ignored + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture); + ASSERT_EQ(0u, args.size()); + + // New gesture should not be affected + currentTime += 100; + moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10); + args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); +} + } // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c2e67fa788..168e74ca83 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -16,6 +16,7 @@ #include "../dispatcher/InputDispatcher.h" #include "FakeApplicationHandle.h" +#include "FakeInputTracingBackend.h" #include "TestEventMatchers.h" #include <NotifyArgsBuilders.h> @@ -154,7 +155,21 @@ static KeyEvent getTestKeyEvent() { class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { struct AnrResult { sp<IBinder> token{}; - gui::Pid pid{gui::Pid::INVALID}; + std::optional<gui::Pid> pid{}; + }; + /* Stores data about a user-activity-poke event from the dispatcher. */ + struct UserActivityPokeEvent { + nsecs_t eventTime; + int32_t eventType; + int32_t displayId; + + bool operator==(const UserActivityPokeEvent& rhs) const = default; + + friend std::ostream& operator<<(std::ostream& os, const UserActivityPokeEvent& ev) { + os << "UserActivityPokeEvent[time=" << ev.eventTime << ", eventType=" << ev.eventType + << ", displayId=" << ev.displayId << "]"; + return os; + } }; public: @@ -245,7 +260,7 @@ public: void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout, const sp<IBinder>& expectedToken, - gui::Pid expectedPid) { + std::optional<gui::Pid> expectedPid) { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); AnrResult result; @@ -265,7 +280,7 @@ public: } void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken, - gui::Pid expectedPid) { + std::optional<gui::Pid> expectedPid) { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); AnrResult result; @@ -349,16 +364,40 @@ public: mInterceptKeyTimeout = timeout; } + std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override { return 500ms; } + void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; } - void assertUserActivityPoked() { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked"; + void assertUserActivityNotPoked() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + std::optional<UserActivityPokeEvent> pokeEvent = + getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, + mNotifyUserActivity); + + ASSERT_FALSE(pokeEvent) << "Expected user activity not to have been poked"; } - void assertUserActivityNotPoked() { - std::scoped_lock lock(mLock); - ASSERT_FALSE(mPokedUserActivity) << "Expected user activity not to have been poked"; + /** + * Asserts that a user activity poke has happened. The earliest recorded poke event will be + * cleared after this call. + * + * If an expected UserActivityPokeEvent is provided, asserts that the given event is the + * earliest recorded poke event. + */ + void assertUserActivityPoked(std::optional<UserActivityPokeEvent> expectedPokeEvent = {}) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + std::optional<UserActivityPokeEvent> pokeEvent = + getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, + mNotifyUserActivity); + ASSERT_TRUE(pokeEvent) << "Expected a user poke event"; + + if (expectedPokeEvent) { + ASSERT_EQ(expectedPokeEvent, *pokeEvent); + } } void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids) { @@ -414,7 +453,9 @@ private: sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock); bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; - bool mPokedUserActivity GUARDED_BY(mLock) = false; + + std::condition_variable mNotifyUserActivity; + std::queue<UserActivityPokeEvent> mUserActivityPokeEvents; std::chrono::milliseconds mInterceptKeyTimeout = 0ms; @@ -483,16 +524,14 @@ private: void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid, const std::string&) override { std::scoped_lock lock(mLock); - ASSERT_TRUE(pid.has_value()); - mAnrWindows.push({connectionToken, *pid}); + mAnrWindows.push({connectionToken, pid}); mNotifyAnr.notify_all(); } void notifyWindowResponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid) override { std::scoped_lock lock(mLock); - ASSERT_TRUE(pid.has_value()); - mResponsiveWindows.push({connectionToken, *pid}); + mResponsiveWindows.push({connectionToken, pid}); mNotifyAnr.notify_all(); } @@ -549,7 +588,7 @@ private: } } - void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} + void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {} nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override { nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count(); @@ -572,12 +611,14 @@ private: /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is * essentially a passthrough for notifySwitch. */ - mLastNotifySwitch = NotifySwitchArgs(/*id=*/1, when, policyFlags, switchValues, switchMask); + mLastNotifySwitch = + NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask); } - void pokeUserActivity(nsecs_t, int32_t, int32_t) override { + void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override { std::scoped_lock lock(mLock); - mPokedUserActivity = true; + mNotifyUserActivity.notify_all(); + mUserActivityPokeEvents.push({eventTime, eventType, displayId}); } bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override { @@ -618,14 +659,22 @@ private: // --- InputDispatcherTest --- +// The trace is a global variable for now, to avoid having to pass it into all of the +// FakeWindowHandles created throughout the tests. +// TODO(b/210460522): Update the tests to avoid the need to have the trace be a global variable. +static std::shared_ptr<VerifyingTrace> gVerifyingTrace = std::make_shared<VerifyingTrace>(); + class InputDispatcherTest : public testing::Test { protected: std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy; std::unique_ptr<InputDispatcher> mDispatcher; void SetUp() override { + gVerifyingTrace->reset(); mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); - mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy); + mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, + std::make_unique<FakeInputTracingBackend>( + gVerifyingTrace)); mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); // Start InputDispatcher thread @@ -633,6 +682,7 @@ protected: } void TearDown() override { + ASSERT_NO_FATAL_FAILURE(gVerifyingTrace->verifyExpectedEventsTraced()); ASSERT_EQ(OK, mDispatcher->stop()); mFakePolicy.reset(); mDispatcher.reset(); @@ -836,7 +886,8 @@ TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) { } TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { - NotifySwitchArgs args(/*id=*/10, /*eventTime=*/20, /*policyFlags=*/0, /*switchValues=*/1, + NotifySwitchArgs args(InputEvent::nextId(), /*eventTime=*/20, /*policyFlags=*/0, + /*switchValues=*/1, /*switchMask=*/2); mDispatcher->notifySwitch(args); @@ -859,30 +910,31 @@ public: explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name) : mConsumer(std::move(clientChannel)), mName(name) {} - InputEvent* consume(std::chrono::milliseconds timeout, bool handled = false) { - InputEvent* event; - std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event); + std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = false) { + auto [consumeSeq, event] = receiveEvent(timeout); if (!consumeSeq) { return nullptr; } finishEvent(*consumeSeq, handled); - return event; + return std::move(event); } /** * Receive an event without acknowledging it. * Return the sequence number that could later be used to send finished signal. */ - std::optional<uint32_t> receiveEvent(std::chrono::milliseconds timeout, - InputEvent** outEvent = nullptr) { + std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent( + std::chrono::milliseconds timeout) { uint32_t consumeSeq; - InputEvent* event; + std::unique_ptr<InputEvent> event; std::chrono::time_point start = std::chrono::steady_clock::now(); status_t status = WOULD_BLOCK; while (status == WOULD_BLOCK) { + InputEvent* rawEventPtr = nullptr; status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, - &event); + &rawEventPtr); + event = std::unique_ptr<InputEvent>(rawEventPtr); std::chrono::duration elapsed = std::chrono::steady_clock::now() - start; if (elapsed > timeout) { break; @@ -891,21 +943,17 @@ public: if (status == WOULD_BLOCK) { // Just means there's no event available. - return std::nullopt; + return std::make_pair(std::nullopt, nullptr); } if (status != OK) { ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK."; - return std::nullopt; + return std::make_pair(std::nullopt, nullptr); } if (event == nullptr) { ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer"; - return std::nullopt; - } - if (outEvent != nullptr) { - *outEvent = event; } - return consumeSeq; + return std::make_pair(consumeSeq, std::move(event)); } /** @@ -924,7 +972,7 @@ public: void consumeEvent(InputEventType expectedEventType, int32_t expectedAction, std::optional<int32_t> expectedDisplayId, std::optional<int32_t> expectedFlags) { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; @@ -968,8 +1016,8 @@ public: } } - MotionEvent* consumeMotion() { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + std::unique_ptr<MotionEvent> consumeMotion() { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); if (event == nullptr) { ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one."; @@ -980,17 +1028,17 @@ public: ADD_FAILURE() << mName << " expected a MotionEvent, got " << *event; return nullptr; } - return static_cast<MotionEvent*>(event); + return std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release())); } void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) { - MotionEvent* motionEvent = consumeMotion(); + std::unique_ptr<MotionEvent> motionEvent = consumeMotion(); ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher; ASSERT_THAT(*motionEvent, matcher); } void consumeFocusEvent(bool hasFocus, bool inTouchMode) { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(InputEventType::FOCUS, event->getType()) @@ -999,12 +1047,12 @@ public: ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) << mName.c_str() << ": event displayId should always be NONE."; - FocusEvent* focusEvent = static_cast<FocusEvent*>(event); - EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + FocusEvent& focusEvent = static_cast<FocusEvent&>(*event); + EXPECT_EQ(hasFocus, focusEvent.getHasFocus()); } void consumeCaptureEvent(bool hasCapture) { - const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(InputEventType::CAPTURE, event->getType()) @@ -1018,7 +1066,7 @@ public: } void consumeDragEvent(bool isExiting, float x, float y) { - const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event; @@ -1033,7 +1081,7 @@ public: } void consumeTouchModeEvent(bool inTouchMode) { - const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType()) @@ -1045,8 +1093,8 @@ public: EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode()); } - void assertNoEvents() { - InputEvent* event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED); + void assertNoEvents(std::chrono::milliseconds timeout) { + std::unique_ptr<InputEvent> event = consume(timeout); if (event == nullptr) { return; } @@ -1075,11 +1123,11 @@ public: sp<IBinder> getToken() { return mConsumer.getChannel()->getConnectionToken(); } - int getChannelFd() { return mConsumer.getChannel()->getFd().get(); } + int getChannelFd() { return mConsumer.getChannel()->getFd(); } private: InputConsumer mConsumer; - PreallocatedInputEventFactory mEventFactory; + DynamicInputEventFactory mEventFactory; std::string mName; }; @@ -1091,9 +1139,10 @@ public: FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name, - int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt) + int32_t displayId, bool createInputChannel = true) : mName(name) { - if (token == std::nullopt) { + sp<IBinder> token; + if (createInputChannel) { base::Result<std::unique_ptr<InputChannel>> channel = dispatcher->createInputChannel(name); token = (*channel)->getConnectionToken(); @@ -1103,7 +1152,7 @@ public: inputApplicationHandle->updateInfo(); mInfo.applicationInfo = *inputApplicationHandle->getInfo(); - mInfo.token = *token; + mInfo.token = token; mInfo.id = sId++; mInfo.name = name; mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; @@ -1182,6 +1231,11 @@ public: mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity); } + void setGlobalStylusBlocksTouch(bool shouldGlobalStylusBlockTouch) { + mInfo.setInputConfig(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH, + shouldGlobalStylusBlockTouch); + } + void setAlpha(float alpha) { mInfo.alpha = alpha; } void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; } @@ -1221,31 +1275,33 @@ public: void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); } - KeyEvent* consumeKey(bool handled = true) { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled); + std::unique_ptr<KeyEvent> consumeKey(bool handled = true) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled); if (event == nullptr) { - ADD_FAILURE() << "Consume failed : no event"; + ADD_FAILURE() << "No event"; return nullptr; } if (event->getType() != InputEventType::KEY) { - ADD_FAILURE() << "Instead of key event, got " << *event; + ADD_FAILURE() << "Instead of key event, got " << event; return nullptr; } - return static_cast<KeyEvent*>(event); + return std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event.release())); } void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) { - KeyEvent* keyEvent = consumeKey(); - ASSERT_NE(nullptr, keyEvent) << "Did not get a key event, but expected " << matcher; + std::unique_ptr<KeyEvent> keyEvent = consumeKey(); + ASSERT_NE(nullptr, keyEvent); ASSERT_THAT(*keyEvent, matcher); } void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); + consumeKeyEvent(AllOf(WithKeyAction(ACTION_DOWN), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags); + consumeKeyEvent(AllOf(WithKeyAction(ACTION_UP), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, @@ -1267,44 +1323,46 @@ public: void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt, std::optional<int32_t> expectedFlags = std::nullopt) { - consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, - expectedFlags); + consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), + testing::Conditional(expectedDisplayId.has_value(), + WithDisplayId(*expectedDisplayId), testing::_), + testing::Conditional(expectedFlags.has_value(), WithFlags(*expectedFlags), + testing::_))); } void consumeMotionPointerDown(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { - int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN | + const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - consumeEvent(InputEventType::MOTION, action, expectedDisplayId, expectedFlags); + consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { - int32_t action = AMOTION_EVENT_ACTION_POINTER_UP | + const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - consumeEvent(InputEventType::MOTION, action, expectedDisplayId, expectedFlags); + consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { - consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId, - expectedFlags); + consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); } void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { - consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, expectedDisplayId, - expectedFlags); + consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), + WithDisplayId(expectedDisplayId), WithFlags(expectedFlags))); } - void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - MotionEvent* motionEvent = consumeMotion(); - ASSERT_NE(nullptr, motionEvent); - EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent->getActionMasked()); - EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getX()); - EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getY()); + void consumeMotionOutsideWithZeroedCoords() { + consumeMotionEvent( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), WithRawCoords(0, 0))); } void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) { @@ -1319,21 +1377,21 @@ public: mInputReceiver->consumeCaptureEvent(hasCapture); } - const MotionEvent& consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) { - MotionEvent* motionEvent = consumeMotion(); - if (nullptr == motionEvent) { - LOG(FATAL) << "Did not get a motion event, but expected " << matcher; + std::unique_ptr<MotionEvent> consumeMotionEvent( + const ::testing::Matcher<MotionEvent>& matcher = testing::_) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + if (event == nullptr) { + ADD_FAILURE() << "No event"; + return nullptr; } + if (event->getType() != InputEventType::MOTION) { + ADD_FAILURE() << "Instead of motion event, got " << *event; + return nullptr; + } + std::unique_ptr<MotionEvent> motionEvent = + std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release())); EXPECT_THAT(*motionEvent, matcher); - return *motionEvent; - } - - void consumeEvent(InputEventType expectedEventType, int32_t expectedAction, - std::optional<int32_t> expectedDisplayId, - std::optional<int32_t> expectedFlags) { - ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver"; - mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId, - expectedFlags); + return motionEvent; } void consumeDragEvent(bool isExiting, float x, float y) { @@ -1346,12 +1404,8 @@ public: mInputReceiver->consumeTouchModeEvent(inTouchMode); } - std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) { - if (mInputReceiver == nullptr) { - ADD_FAILURE() << "Invalid receive event on window with no receiver"; - return std::nullopt; - } - return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED, outEvent); + std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent() { + return receive(); } void finishEvent(uint32_t sequenceNum) { @@ -1364,34 +1418,14 @@ public: mInputReceiver->sendTimeline(inputEventId, timeline); } - InputEvent* consume(std::chrono::milliseconds timeout, bool handled = true) { - if (mInputReceiver == nullptr) { - return nullptr; - } - return mInputReceiver->consume(timeout, handled); - } - - MotionEvent* consumeMotion() { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - if (event == nullptr) { - ADD_FAILURE() << "Consume failed : no event"; - return nullptr; - } - if (event->getType() != InputEventType::MOTION) { - ADD_FAILURE() << "Instead of motion event, got " << *event; - return nullptr; - } - return static_cast<MotionEvent*>(event); - } - - void assertNoEvents() { + void assertNoEvents(std::chrono::milliseconds timeout = CONSUME_TIMEOUT_NO_EVENT_EXPECTED) { if (mInputReceiver == nullptr && mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { return; // Can't receive events if the window does not have input channel } ASSERT_NE(nullptr, mInputReceiver) << "Window without InputReceiver must specify feature NO_INPUT_CHANNEL"; - mInputReceiver->assertNoEvents(); + mInputReceiver->assertNoEvents(timeout); } sp<IBinder> getToken() { return mInfo.token; } @@ -1409,12 +1443,57 @@ public: int getChannelFd() { return mInputReceiver->getChannelFd(); } + // FakeWindowHandle uses this consume method to ensure received events are added to the trace. + std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true) { + if (mInputReceiver == nullptr) { + LOG(FATAL) << "Cannot consume event from a window with no input event receiver"; + } + std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled); + if (event == nullptr) { + ADD_FAILURE() << "Consume failed: no event"; + } + expectReceivedEventTraced(event); + return event; + } + private: FakeWindowHandle(std::string name) : mName(name){}; const std::string mName; std::shared_ptr<FakeInputReceiver> mInputReceiver; static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger friend class sp<FakeWindowHandle>; + + // FakeWindowHandle uses this receive method to ensure received events are added to the trace. + std::pair<std::optional<uint32_t /*seq*/>, std::unique_ptr<InputEvent>> receive() { + if (mInputReceiver == nullptr) { + ADD_FAILURE() << "Invalid receive event on window with no receiver"; + return std::make_pair(std::nullopt, nullptr); + } + auto out = mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED); + const auto& [_, event] = out; + expectReceivedEventTraced(event); + return std::move(out); + } + + void expectReceivedEventTraced(const std::unique_ptr<InputEvent>& event) { + if (!event) { + return; + } + + switch (event->getType()) { + case InputEventType::KEY: { + gVerifyingTrace->expectKeyDispatchTraced(static_cast<KeyEvent&>(*event), mInfo.id); + break; + } + case InputEventType::MOTION: { + gVerifyingTrace->expectMotionDispatchTraced(static_cast<MotionEvent&>(*event), + mInfo.id); + break; + } + default: + break; + } + } }; std::atomic<int32_t> FakeWindowHandle::sId{1}; @@ -1432,7 +1511,8 @@ public: } std::optional<int32_t> receiveEvent() { - return mInputReceiver.receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED); + const auto [sequenceNum, _] = mInputReceiver.receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED); + return sequenceNum; } void finishEvent(uint32_t consumeSeq) { return mInputReceiver.finishEvent(consumeSeq); } @@ -1470,9 +1550,9 @@ public: mInputReceiver.consumeMotionEvent(matcher); } - MotionEvent* consumeMotion() { return mInputReceiver.consumeMotion(); } + std::unique_ptr<MotionEvent> consumeMotion() { return mInputReceiver.consumeMotion(); } - void assertNoEvents() { mInputReceiver.assertNoEvents(); } + void assertNoEvents() { mInputReceiver.assertNoEvents(CONSUME_TIMEOUT_NO_EVENT_EXPECTED); } private: FakeInputReceiver mInputReceiver; @@ -1579,9 +1659,9 @@ static InputEventInjectionResult injectMotionUp(InputDispatcher& dispatcher, int static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. - NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - displayId, POLICY_FLAG_PASS_TO_USER, action, /*flags=*/0, AKEYCODE_A, KEY_A, - AMETA_NONE, currentTime); + NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, + AINPUT_SOURCE_KEYBOARD, displayId, POLICY_FLAG_PASS_TO_USER, action, + /*flags=*/0, AKEYCODE_A, KEY_A, AMETA_NONE, currentTime); return args; } @@ -1590,9 +1670,9 @@ static NotifyKeyArgs generateSystemShortcutArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. - NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, AMETA_META_ON, - currentTime); + NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, + AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, + AMETA_META_ON, currentTime); return args; } @@ -1601,9 +1681,9 @@ static NotifyKeyArgs generateAssistantKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. - NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, KEY_ASSISTANT, - AMETA_NONE, currentTime); + NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, + AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, + KEY_ASSISTANT, AMETA_NONE, currentTime); return args; } @@ -1631,9 +1711,9 @@ static NotifyKeyArgs generateAssistantKeyArgs(int32_t action, nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion event. - NotifyMotionArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, source, displayId, - POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, /*flags=*/0, - AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, + NotifyMotionArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, source, + displayId, POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, + /*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties, pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0, AMOTION_EVENT_INVALID_CURSOR_POSITION, @@ -1652,7 +1732,8 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs( const PointerCaptureRequest& request) { - return NotifyPointerCaptureChangedArgs(/*id=*/0, systemTime(SYSTEM_TIME_MONOTONIC), request); + return NotifyPointerCaptureChangedArgs(InputEvent::nextId(), systemTime(SYSTEM_TIME_MONOTONIC), + request); } } // namespace @@ -1688,6 +1769,24 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { window->consumeMotionDown(ADISPLAY_ID_DEFAULT); } +using InputDispatcherDeathTest = InputDispatcherTest; + +/** + * When 'onWindowInfosChanged' arguments contain a duplicate entry for the same window, dispatcher + * should crash. + */ +TEST_F(InputDispatcherDeathTest, DuplicateWindowInfosAbortDispatcher) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + ScopedSilentDeath _silentDeath; + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); + ASSERT_DEATH(mDispatcher->onWindowInfosChanged( + {{*window->getInfo(), *window->getInfo()}, {}, 0, 0}), + "Incorrect WindowInfosUpdate provided"); +} + TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, @@ -2379,7 +2478,7 @@ TEST_F(InputDispatcherTest, HoverWhileWindowAppears) { sp<FakeWindowHandle> obscuringWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window", ADISPLAY_ID_DEFAULT, - /*token=*/std::make_optional<sp<IBinder>>(nullptr)); + /*createInputChannel=*/false); obscuringWindow->setFrame(Rect(0, 0, 200, 200)); obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); obscuringWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID); @@ -2428,7 +2527,7 @@ TEST_F(InputDispatcherTest, HoverMoveWhileWindowAppears) { sp<FakeWindowHandle> obscuringWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window", ADISPLAY_ID_DEFAULT, - /*token=*/std::make_optional<sp<IBinder>>(nullptr)); + /*createInputChannel=*/false); obscuringWindow->setFrame(Rect(0, 0, 200, 200)); obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); obscuringWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID); @@ -3355,6 +3454,150 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverIgnoresTouchTap) { } /** + * If stylus is down anywhere on the screen, then touches should not be delivered to windows that + * have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH. + * + * Two windows: one on the left and one on the right. + * The window on the right has GLOBAL_STYLUS_BLOCKS_TOUCH config. + * Stylus down on the left window, and then touch down on the right window. + * Check that the right window doesn't get touches while the stylus is down on the left window. + */ +TEST_F(InputDispatcherMultiDeviceTest, GlobalStylusDownBlocksTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> leftWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Left window", + ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 100, 100)); + + sp<FakeWindowHandle> sbtRightWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, + "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT); + sbtRightWindow->setFrame(Rect(100, 100, 200, 200)); + sbtRightWindow->setGlobalStylusBlocksTouch(true); + + mDispatcher->onWindowInfosChanged( + {{*leftWindow->getInfo(), *sbtRightWindow->getInfo()}, {}, 0, 0}); + + const int32_t stylusDeviceId = 5; + const int32_t touchDeviceId = 4; + + // Stylus down in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(52)) + .deviceId(stylusDeviceId) + .build()); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId))); + + // Finger tap on the right window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151)) + .deviceId(touchDeviceId) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151)) + .deviceId(touchDeviceId) + .build()); + + // The touch should be blocked, because stylus is down somewhere else on screen! + sbtRightWindow->assertNoEvents(); + + // Continue stylus motion, and ensure it's not impacted. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53)) + .deviceId(stylusDeviceId) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53)) + .deviceId(stylusDeviceId) + .build()); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId))); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_UP), WithDeviceId(stylusDeviceId))); + + // Now that the stylus gesture is done, touches should be getting delivered correctly. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(153)) + .deviceId(touchDeviceId) + .build()); + sbtRightWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); +} + +/** + * If stylus is hovering anywhere on the screen, then touches should not be delivered to windows + * that have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH. + * + * Two windows: one on the left and one on the right. + * The window on the right has GLOBAL_STYLUS_BLOCKS_TOUCH config. + * Stylus hover on the left window, and then touch down on the right window. + * Check that the right window doesn't get touches while the stylus is hovering on the left window. + */ +TEST_F(InputDispatcherMultiDeviceTest, GlobalStylusHoverBlocksTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> leftWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Left window", + ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 100, 100)); + + sp<FakeWindowHandle> sbtRightWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, + "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT); + sbtRightWindow->setFrame(Rect(100, 100, 200, 200)); + sbtRightWindow->setGlobalStylusBlocksTouch(true); + + mDispatcher->onWindowInfosChanged( + {{*leftWindow->getInfo(), *sbtRightWindow->getInfo()}, {}, 0, 0}); + + const int32_t stylusDeviceId = 5; + const int32_t touchDeviceId = 4; + + // Stylus hover in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(52)) + .deviceId(stylusDeviceId) + .build()); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId))); + + // Finger tap on the right window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151)) + .deviceId(touchDeviceId) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151)) + .deviceId(touchDeviceId) + .build()); + + // The touch should be blocked, because stylus is hovering somewhere else on screen! + sbtRightWindow->assertNoEvents(); + + // Continue stylus motion, and ensure it's not impacted. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53)) + .deviceId(stylusDeviceId) + .build()); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53)) + .deviceId(stylusDeviceId) + .build()); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId))); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId))); + + // Now that the stylus gesture is done, touches should be getting delivered correctly. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(153)) + .deviceId(touchDeviceId) + .build()); + sbtRightWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); +} + +/** * A spy window above a window with no input channel. * Start hovering with a stylus device, and then tap with it. * Ensure spy window receives the entire sequence. @@ -3622,48 +3865,57 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) { // Touch down on the first window mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}})); - mDispatcher->waitForIdle(); - MotionEvent* motionEvent1 = window1->consumeMotion(); - ASSERT_NE(motionEvent1, nullptr); + const std::unique_ptr<MotionEvent> firstDown = + window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN))); + ASSERT_EQ(firstDown->getDownTime(), firstDown->getEventTime()); window2->assertNoEvents(); - nsecs_t downTimeForWindow1 = motionEvent1->getDownTime(); - ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime()); // Now touch down on the window with another pointer mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})); mDispatcher->waitForIdle(); - MotionEvent* motionEvent2 = window2->consumeMotion(); - ASSERT_NE(motionEvent2, nullptr); - nsecs_t downTimeForWindow2 = motionEvent2->getDownTime(); - ASSERT_NE(downTimeForWindow1, downTimeForWindow2); - ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime()); + + const std::unique_ptr<MotionEvent> secondDown = + window2->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN))); + ASSERT_EQ(secondDown->getDownTime(), secondDown->getEventTime()); + ASSERT_NE(firstDown->getDownTime(), secondDown->getDownTime()); + // We currently send MOVE events to all windows receiving a split touch when there is any change + // in the touch state, even when none of the pointers in the split window actually moved. + // Document this behavior in the test. + window1->consumeMotionMove(); // Now move the pointer on the second window mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})); mDispatcher->waitForIdle(); - window2->consumeMotionEvent(WithDownTime(downTimeForWindow2)); + + window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime())); + window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime())); // Now add new touch down on the second window mDispatcher->notifyMotion(generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}})); mDispatcher->waitForIdle(); - window2->consumeMotionEvent(WithDownTime(downTimeForWindow2)); - // TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line - window1->consumeMotionMove(); - window1->assertNoEvents(); + window2->consumeMotionEvent( + AllOf(WithMotionAction(POINTER_1_DOWN), WithDownTime(secondDown->getDownTime()))); + window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime())); // Now move the pointer on the first window mDispatcher->notifyMotion( generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}, {150, 50}})); mDispatcher->waitForIdle(); - window1->consumeMotionEvent(WithDownTime(downTimeForWindow1)); + window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime())); + window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime())); + + // Now add new touch down on the first window mDispatcher->notifyMotion( generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}})); mDispatcher->waitForIdle(); - window1->consumeMotionEvent(WithDownTime(downTimeForWindow1)); + + window1->consumeMotionEvent( + AllOf(WithMotionAction(POINTER_1_DOWN), WithDownTime(firstDown->getDownTime()))); + window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime())); } TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { @@ -4233,8 +4485,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { // When device reset happens, that key stream should be terminated with FLAG_CANCELED // on the app side. mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID}); - window->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT, - AKEY_EVENT_FLAG_CANCELED); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT, AKEY_EVENT_FLAG_CANCELED); } TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { @@ -4473,7 +4724,8 @@ TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) { InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - const MotionEvent* event = window->consumeMotion(); + std::unique_ptr<MotionEvent> event = window->consumeMotionEvent(); + ASSERT_NE(nullptr, event); EXPECT_EQ(POINTER_1_DOWN, event->getAction()); EXPECT_EQ(70, event->getX(0)); // 50 + 20 EXPECT_EQ(90, event->getY(0)); // 50 + 40 @@ -4744,7 +4996,8 @@ TEST_F(InputDispatcherTest, WhenMultiDisplayWindowSameToken_DispatchCancelToTarg EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindowDefaultDisplay->getToken())); // windowDefaultDisplay gets cancel - MotionEvent* event = windowDefaultDisplay->consumeMotion(); + std::unique_ptr<MotionEvent> event = windowDefaultDisplay->consumeMotionEvent(); + ASSERT_NE(nullptr, event); EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event->getAction()); // The cancel event is sent to windowDefaultDisplay of the ADISPLAY_ID_DEFAULT display, so the @@ -4882,7 +5135,7 @@ TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinate {PointF{150, 220}})); firstWindow->assertNoEvents(); - const MotionEvent* event = secondWindow->consumeMotion(); + std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent(); ASSERT_NE(nullptr, event); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction()); @@ -4937,7 +5190,7 @@ TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeDownWithCorrectCoordinate // The pointer is transferred to the second window, and the second window receives it in the // correct coordinate space. - mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken()); firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400))); secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(-100, -400))); } @@ -5137,9 +5390,11 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { sp<FakeWindowHandle> wallpaper = sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaper->setIsWallpaper(true); - // Add the windows to the dispatcher + // Add the windows to the dispatcher, and ensure the first window is focused mDispatcher->onWindowInfosChanged( {{*firstWindow->getInfo(), *secondWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0}); + setFocusedWindow(firstWindow); + firstWindow->consumeFocusEvent(true); // Send down to the first window mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, @@ -5149,6 +5404,8 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { firstWindow->consumeMotionDown(); secondWindow->assertNoEvents(); wallpaper->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + // Dispatcher reports pointer down outside focus for the wallpaper + mFakePolicy->assertOnPointerDownEquals(wallpaper->getToken()); // Transfer touch to the second window TransferFunction f = GetParam(); @@ -5156,29 +5413,31 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { ASSERT_TRUE(success); // The first window gets cancel and the second gets down firstWindow->consumeMotionCancel(); - secondWindow->consumeMotionDown(); + secondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); wallpaper->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + // There should not be any changes to the focused window when transferring touch + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertOnPointerDownWasNotCalled()); // Send up event to the second window mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); - // The first window gets no events and the second gets up + // The first window gets no events and the second gets up firstWindow->assertNoEvents(); - secondWindow->consumeMotionUp(); + secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); wallpaper->assertNoEvents(); } /** - * When 'transferTouch' API is invoked, dispatcher needs to find the "best" window to take touch - * from. When we have spy windows, there are several windows to choose from: either spy, or the - * 'real' (non-spy) window. Always prefer the 'real' window because that's what would be most + * When 'transferTouchGesture' API is invoked, dispatcher needs to find the "best" window to take + * touch from. When we have spy windows, there are several windows to choose from: either spy, or + * the 'real' (non-spy) window. Always prefer the 'real' window because that's what would be most * natural to the user. * In this test, we are sending a pointer to both spy window and first window. We then try to * transfer touch to the second window. The dispatcher should identify the first window as the * one that should lose the gesture, and therefore the action should be to move the gesture from * the first window to the second. - * The main goal here is to test the behaviour of 'transferTouch' API, but it's still valid to test - * the other API, as well. + * The main goal here is to test the behaviour of 'transferTouchGesture' API, but it's still valid + * to test the other API, as well. */ TEST_P(TransferTouchFixture, TransferTouch_MultipleWindowsWithSpy) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -5205,13 +5464,13 @@ TEST_P(TransferTouchFixture, TransferTouch_MultipleWindowsWithSpy) { firstWindow->consumeMotionDown(); // Transfer touch to the second window. Non-spy window should be preferred over the spy window - // if f === 'transferTouch'. + // if f === 'transferTouchGesture'. TransferFunction f = GetParam(); const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken()); ASSERT_TRUE(success); // The first window gets cancel and the second gets down firstWindow->consumeMotionCancel(); - secondWindow->consumeMotionDown(); + secondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); // Send up event to the second window mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, @@ -5219,7 +5478,7 @@ TEST_P(TransferTouchFixture, TransferTouch_MultipleWindowsWithSpy) { // The first window gets no events and the second+spy get up firstWindow->assertNoEvents(); spyWindow->consumeMotionUp(); - secondWindow->consumeMotionUp(); + secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { @@ -5262,22 +5521,24 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { ASSERT_TRUE(success); // The first window gets cancel and the second gets down and pointer down firstWindow->consumeMotionCancel(); - secondWindow->consumeMotionDown(); - secondWindow->consumeMotionPointerDown(1); + secondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + secondWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); // Send pointer up to the second window mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint})); // The first window gets nothing and the second gets pointer up firstWindow->assertNoEvents(); - secondWindow->consumeMotionPointerUp(1); + secondWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); // Send up event to the second window mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); // The first window gets nothing and the second gets up firstWindow->assertNoEvents(); - secondWindow->consumeMotionUp(); + secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } TEST_P(TransferTouchFixture, TransferTouch_MultipleWallpapers) { @@ -5324,37 +5585,40 @@ TEST_P(TransferTouchFixture, TransferTouch_MultipleWallpapers) { // The first window gets cancel and the second gets down firstWindow->consumeMotionCancel(); - secondWindow->consumeMotionDown(); + secondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); wallpaper1->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT, + expectedWallpaperFlags | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); // Send up event to the second window mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); // The first window gets no events and the second gets up firstWindow->assertNoEvents(); - secondWindow->consumeMotionUp(); + secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); wallpaper1->assertNoEvents(); - wallpaper2->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + wallpaper2->consumeMotionUp(ADISPLAY_ID_DEFAULT, + expectedWallpaperFlags | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } // For the cases of single pointer touch and two pointers non-split touch, the api's -// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ +// 'transferTouchGesture' and 'transferTouchOnDisplay' are equivalent in behaviour. They only differ // for the case where there are multiple pointers split across several windows. -INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture, - ::testing::Values( - [&](const std::unique_ptr<InputDispatcher>& dispatcher, - sp<IBinder> /*ignored*/, sp<IBinder> destChannelToken) { - return dispatcher->transferTouch(destChannelToken, - ADISPLAY_ID_DEFAULT); - }, - [&](const std::unique_ptr<InputDispatcher>& dispatcher, - sp<IBinder> from, sp<IBinder> to) { - return dispatcher->transferTouchFocus(from, to, - /*isDragAndDrop=*/false); - })); - -TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { +INSTANTIATE_TEST_SUITE_P( + InputDispatcherTransferFunctionTests, TransferTouchFixture, + ::testing::Values( + [&](const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder> /*ignored*/, + sp<IBinder> destChannelToken) { + return dispatcher->transferTouchOnDisplay(destChannelToken, + ADISPLAY_ID_DEFAULT); + }, + [&](const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder> from, + sp<IBinder> to) { + return dispatcher->transferTouchGesture(from, to, + /*isDragAndDrop=*/false); + })); + +TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindow = @@ -5390,11 +5654,12 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { firstWindow->consumeMotionMove(); secondWindow->consumeMotionDown(); - // Transfer touch focus to the second window - mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + // Transfer touch to the second window + mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken()); // The first window gets cancel and the new gets pointer down (it already saw down) firstWindow->consumeMotionCancel(); - secondWindow->consumeMotionPointerDown(1); + secondWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); // Send pointer up to the second window mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, @@ -5402,21 +5667,22 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { {pointInFirst, pointInSecond})); // The first window gets nothing and the second gets pointer up firstWindow->assertNoEvents(); - secondWindow->consumeMotionPointerUp(1); + secondWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); // Send up event to the second window mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); // The first window gets nothing and the second gets up firstWindow->assertNoEvents(); - secondWindow->consumeMotionUp(); + secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } -// Same as TransferTouchFocus_TwoPointersSplitTouch, but using 'transferTouch' api. -// Unlike 'transferTouchFocus', calling 'transferTouch' when there are two windows receiving -// touch is not supported, so the touch should continue on those windows and the transferred-to -// window should get nothing. -TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { +// Same as TransferTouch_TwoPointersSplitTouch, but using 'transferTouchOnDisplay' api. +// Unlike 'transferTouchGesture', calling 'transferTouchOnDisplay' when there are two windows +// receiving touch is not supported, so the touch should continue on those windows and the +// transferred-to window should get nothing. +TEST_F(InputDispatcherTest, TransferTouchOnDisplay_TwoPointersSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindow = @@ -5454,8 +5720,8 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { // Transfer touch focus to the second window const bool transferred = - mDispatcher->transferTouch(secondWindow->getToken(), ADISPLAY_ID_DEFAULT); - // The 'transferTouch' call should not succeed, because there are 2 touched windows + mDispatcher->transferTouchOnDisplay(secondWindow->getToken(), ADISPLAY_ID_DEFAULT); + // The 'transferTouchOnDisplay' call should not succeed, because there are 2 touched windows ASSERT_FALSE(transferred); firstWindow->assertNoEvents(); secondWindow->assertNoEvents(); @@ -5478,9 +5744,9 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { } // This case will create two windows and one mirrored window on the default display and mirror -// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put +// two windows on the second display. It will test if 'transferTouchGesture' works fine if we put // the windows info of second display before default display. -TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) { +TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindowInPrimary = sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); @@ -5515,31 +5781,33 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) { // Window should receive motion event. firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); - // Transfer touch focus - ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(), - secondWindowInPrimary->getToken())); + // Transfer touch + ASSERT_TRUE(mDispatcher->transferTouchGesture(firstWindowInPrimary->getToken(), + secondWindowInPrimary->getToken())); // The first window gets cancel. firstWindowInPrimary->consumeMotionCancel(); - secondWindowInPrimary->consumeMotionDown(); + secondWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; firstWindowInPrimary->assertNoEvents(); - secondWindowInPrimary->consumeMotionMove(); + secondWindowInPrimary->consumeMotionMove(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; firstWindowInPrimary->assertNoEvents(); - secondWindowInPrimary->consumeMotionUp(); + secondWindowInPrimary->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } -// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use -// 'transferTouch' api. -TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { +// Same as TransferTouch_CloneSurface, but this touch on the secondary display and use +// 'transferTouchOnDisplay' api. +TEST_F(InputDispatcherTest, TransferTouchOnDisplay_CloneSurface) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> firstWindowInPrimary = sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); @@ -5573,27 +5841,30 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Window should receive motion event. - firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID); + firstWindowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); // Transfer touch focus - ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken(), SECOND_DISPLAY_ID)); + ASSERT_TRUE(mDispatcher->transferTouchOnDisplay(secondWindowInSecondary->getToken(), + SECOND_DISPLAY_ID)); // The first window gets cancel. - firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID); - secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID); + firstWindowInSecondary->consumeMotionCancel(SECOND_DISPLAY_ID); + secondWindowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - firstWindowInPrimary->assertNoEvents(); - secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID); + firstWindowInSecondary->assertNoEvents(); + secondWindowInSecondary->consumeMotionMove(SECOND_DISPLAY_ID, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - firstWindowInPrimary->assertNoEvents(); - secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID); + firstWindowInSecondary->assertNoEvents(); + secondWindowInSecondary->consumeMotionUp(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { @@ -5931,7 +6202,8 @@ TEST_F(InputDispatcherMonitorTest, NoWindowTransform) { injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - MotionEvent* event = monitor.consumeMotion(); + std::unique_ptr<MotionEvent> event = monitor.consumeMotion(); + ASSERT_NE(nullptr, event); // Even though window has transform, gesture monitor must not. ASSERT_EQ(ui::Transform(), event->getTransform()); } @@ -5946,6 +6218,219 @@ TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) { monitor.assertNoEvents(); } +/** + * Two displays + * The first monitor has a foreground window, a monitor + * The second window has only one monitor. + * We first inject a Down event into the first display, this injection should succeed and both + * the foreground window and monitor should receive a down event, then inject a Down event into + * the second display as well, this injection should fail, at this point, the first display + * window and monitor should not receive a cancel or any other event. + * Continue to inject Move and UP events to the first display, the events should be received + * normally by the foreground window and monitor. + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCanceledWhenAnotherEmptyDisplayReceiveEvents) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID); + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected into the first display should succeed"; + + window->consumeMotionDown(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {100, 200})) + << "The down event injected into the second display should fail since there's no " + "touchable window"; + + // Continue to inject event to first display. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected into the first display should succeed"; + + window->consumeMotionMove(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {110, 220})) + << "The up event injected into the first display should succeed"; + + window->consumeMotionUp(); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + window->assertNoEvents(); + monitor.assertNoEvents(); + secondMonitor.assertNoEvents(); +} + +/** + * Two displays + * There is a monitor and foreground window on each display. + * First, we inject down events into each of the two displays, at this point, the foreground windows + * and monitors on both displays should receive down events. + * At this point, the foreground window of the second display goes away, the gone window should + * receive the cancel event, and the other windows and monitors should not receive any events. + * Inject a move event into the second display. At this point, the injection should fail because + * the second display no longer has a foreground window. At this point, the monitor on the second + * display should receive a cancel event, and any windows or monitors on the first display should + * not receive any events, and any subsequent injection of events into the second display should + * also fail. + * Continue to inject events into the first display, and the events should all be injected + * successfully and received normally. + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCancelWhenAnotherDisplayMonitorTouchCanceled) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> secondWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "SecondForeground", + SECOND_DISPLAY_ID); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID); + + // There is a foreground window on both displays. + mDispatcher->onWindowInfosChanged({{*window->getInfo(), *secondWindow->getInfo()}, {}, 0, 0}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected into the first display should succeed"; + + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {100, 200})) + << "The down event injected into the second display should succeed"; + + secondWindow->consumeMotionDown(SECOND_DISPLAY_ID); + secondMonitor.consumeMotionDown(SECOND_DISPLAY_ID); + + // Now second window is gone away. + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + // The gone window should receive a cancel, and the monitor on the second display should not + // receive any events. + secondWindow->consumeMotionCancel(SECOND_DISPLAY_ID); + secondMonitor.assertNoEvents(); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + SECOND_DISPLAY_ID, {110, 220})) + << "The move event injected into the second display should fail because there's no " + "touchable window"; + // Now the monitor on the second display should receive a cancel event. + secondMonitor.consumeMotionCancel(SECOND_DISPLAY_ID); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 200})) + << "The move event injected into the first display should succeed"; + + window->consumeMotionMove(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {110, 220})) + << "The up event injected into the second display should fail because there's no " + "touchable window"; + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {110, 220})) + << "The up event injected into the first display should succeed"; + + window->consumeMotionUp(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + window->assertNoEvents(); + monitor.assertNoEvents(); + secondWindow->assertNoEvents(); + secondMonitor.assertNoEvents(); +} + +/** + * One display with transform + * There is a foreground window and a monitor on the display + * Inject down event and move event sequentially, the foreground window and monitor can receive down + * event and move event, then let the foreground window go away, the foreground window receives + * cancel event, inject move event again, the monitor receives cancel event, all the events received + * by the monitor should be with the same transform as the display + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchCancelEventWithDisplayTransform) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + + ui::Transform transform; + transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1}); + + gui::DisplayInfo displayInfo; + displayInfo.displayId = ADISPLAY_ID_DEFAULT; + displayInfo.transform = transform; + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {displayInfo}, 0, 0}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected should succeed"; + + window->consumeMotionDown(); + std::unique_ptr<MotionEvent> downMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, downMotionEvent->getTransform()); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, downMotionEvent->getAction()); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected should succeed"; + + window->consumeMotionMove(); + std::unique_ptr<MotionEvent> moveMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, moveMotionEvent->getTransform()); + EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, moveMotionEvent->getAction()); + + // Let foreground window gone + mDispatcher->onWindowInfosChanged({{}, {displayInfo}, 0, 0}); + + // Foreground window should receive a cancel event, but not the monitor. + window->consumeMotionCancel(); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected should failed"; + // Now foreground should not receive any events, but monitor should receive a cancel event + // with transform that same as display's display. + std::unique_ptr<MotionEvent> cancelMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, cancelMotionEvent->getTransform()); + EXPECT_EQ(ADISPLAY_ID_DEFAULT, cancelMotionEvent->getDisplayId()); + EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, cancelMotionEvent->getAction()); + + // Other event inject to this display should fail. + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The up event injected should fail because the touched window was removed"; + window->assertNoEvents(); + monitor.assertNoEvents(); +} + TEST_F(InputDispatcherTest, TestMoveEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, @@ -5968,8 +6453,7 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { motionArgs.pointerCoords[0].getX() - 10); mDispatcher->notifyMotion(motionArgs); - window->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT, - /*expectedFlags=*/0); + window->consumeMotionMove(ADISPLAY_ID_DEFAULT, /*expectedFlags=*/0); } /** @@ -6039,9 +6523,8 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN); mDispatcher->notifyKey(keyArgs); - KeyEvent* event = window->consumeKey(); + std::unique_ptr<KeyEvent> event = window->consumeKey(); ASSERT_NE(event, nullptr); - std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event); ASSERT_NE(verified, nullptr); ASSERT_EQ(verified->type, VerifiedInputEvent::Type::KEY); @@ -6083,9 +6566,8 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { ADISPLAY_ID_DEFAULT); mDispatcher->notifyMotion(motionArgs); - MotionEvent* event = window->consumeMotion(); - ASSERT_NE(event, nullptr); - + std::unique_ptr<MotionEvent> event = window->consumeMotionEvent(); + ASSERT_NE(nullptr, event); std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event); ASSERT_NE(verified, nullptr); ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION); @@ -6613,13 +7095,13 @@ TEST_F(InputDispatcherTest, HoverEnterExitSynthesisUsesNewEventId) { ADISPLAY_ID_DEFAULT, {PointF{50, 50}}); mDispatcher->notifyMotion(notifyArgs); - const MotionEvent& leftEnter = left->consumeMotionEvent( + std::unique_ptr<MotionEvent> leftEnter = left->consumeMotionEvent( AllOf(WithMotionAction(ACTION_HOVER_ENTER), Not(WithEventId(notifyArgs.id)), WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER))); - + ASSERT_NE(nullptr, leftEnter); spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER), Not(WithEventId(notifyArgs.id)), - Not(WithEventId(leftEnter.getId())), + Not(WithEventId(leftEnter->getId())), WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER))); // Send move to the right window, and ensure hover exit and enter are synthesized with new ids. @@ -6627,13 +7109,13 @@ TEST_F(InputDispatcherTest, HoverEnterExitSynthesisUsesNewEventId) { {PointF{150, 50}}); mDispatcher->notifyMotion(notifyArgs); - const MotionEvent& leftExit = left->consumeMotionEvent( + std::unique_ptr<MotionEvent> leftExit = left->consumeMotionEvent( AllOf(WithMotionAction(ACTION_HOVER_EXIT), Not(WithEventId(notifyArgs.id)), WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER))); - + ASSERT_NE(nullptr, leftExit); right->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER), Not(WithEventId(notifyArgs.id)), - Not(WithEventId(leftExit.getId())), + Not(WithEventId(leftExit->getId())), WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER))); spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithEventId(notifyArgs.id))); @@ -6664,8 +7146,8 @@ protected: } void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) { - KeyEvent* event = mWindow->consumeKey(handled); - ASSERT_NE(event, nullptr) << "Did not receive key event"; + std::unique_ptr<KeyEvent> event = mWindow->consumeKey(handled); + ASSERT_NE(nullptr, event); ASSERT_THAT(*event, matcher); } }; @@ -6846,6 +7328,104 @@ TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) { mWindow->assertNoEvents(); } +TEST_F(InputDispatcherFallbackKeyTest, InputChannelRemovedDuringPolicyCall) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + mFakePolicy->setUnhandledKeyHandler([&](const KeyEvent& event) { + // When the unhandled key is reported to the policy next, remove the input channel. + mDispatcher->removeInputChannel(mWindow->getToken()); + return KeyEventBuilder(event).keyCode(AKEYCODE_B).build(); + }); + // Release the original key, and let the app now handle the previously unhandled key. + // This should result in the previously generated fallback key to be cancelled. + // Since the policy was notified of the unhandled DOWN event earlier, it will also be notified + // of the UP event for consistency. The Dispatcher calls into the policy from its own thread + // without holding the lock, because it need to synchronously fetch the fallback key. While in + // the policy call, we will now remove the input channel. Once the policy call returns, the + // Dispatcher will no longer have a channel to send cancellation events to. Ensure this does + // not cause any crashes. + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); +} + +TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + mFakePolicy->setUnhandledKeyHandler([&](const KeyEvent& event) { + // When the unhandled key is reported to the policy next, remove the window. + mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); + return KeyEventBuilder(event).keyCode(AKEYCODE_B).build(); + }); + // Release the original key, which the app will not handle. When this unhandled key is reported + // to the policy, the window will be removed. + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + + // Since the window was removed, it loses focus, and the channel state will be reset. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + mWindow->consumeFocusEvent(false); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedWhileAwaitingFinishedSignal) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + const auto [seq, event] = mWindow->receiveEvent(); + ASSERT_TRUE(seq.has_value() && event != nullptr) << "Failed to receive fallback event"; + ASSERT_EQ(event->getType(), InputEventType::KEY); + ASSERT_THAT(static_cast<const KeyEvent&>(*event), + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Remove the window now, which should generate a cancellations and make the window lose focus. + mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), + WithFlags(AKEY_EVENT_FLAG_CANCELED))); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + mWindow->consumeFocusEvent(false); + + // Finish the event by reporting it as handled. + mWindow->finishEvent(*seq); + mWindow->assertNoEvents(); +} + class InputDispatcherKeyRepeatTest : public InputDispatcherTest { protected: static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms; @@ -6855,12 +7435,9 @@ protected: sp<FakeWindowHandle> mWindow; virtual void SetUp() override { - mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); - mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy); - mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); - mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); - ASSERT_EQ(OK, mDispatcher->start()); + InputDispatcherTest::SetUp(); + mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); setUpWindow(); } @@ -6897,7 +7474,7 @@ protected: mDispatcher->notifyKey(keyArgs); // Window should receive key down event. - mWindow->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT, + mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT, /*expectedFlags=*/0); } }; @@ -6967,8 +7544,8 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFrom GTEST_SKIP() << "Flaky test (b/270393106)"; sendAndConsumeKeyDown(/*deviceId=*/1); for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { - KeyEvent* repeatEvent = mWindow->consumeKey(); - ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; + std::unique_ptr<KeyEvent> repeatEvent = mWindow->consumeKey(); + ASSERT_NE(nullptr, repeatEvent); EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER, IdGenerator::getSource(repeatEvent->getId())); } @@ -6980,8 +7557,8 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEvent std::unordered_set<int32_t> idSet; for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { - KeyEvent* repeatEvent = mWindow->consumeKey(); - ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; + std::unique_ptr<KeyEvent> repeatEvent = mWindow->consumeKey(); + ASSERT_NE(nullptr, repeatEvent); int32_t id = repeatEvent->getId(); EXPECT_EQ(idSet.end(), idSet.find(id)); idSet.insert(id); @@ -7071,8 +7648,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0}); // Old focus should receive a cancel event. - windowInSecondary->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE, - AKEY_EVENT_FLAG_CANCELED); + windowInSecondary->consumeKeyUp(ADISPLAY_ID_NONE, AKEY_EVENT_FLAG_CANCELED); // Test inject a key down, should timeout because of no target window. ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher)); @@ -7208,6 +7784,94 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) { monitorInSecondary.assertNoEvents(); } +/** + * Send a key to the primary display and to the secondary display. + * Then cause the key on the primary display to be canceled by sending in a stale key. + * Ensure that the key on the primary display is canceled, and that the key on the secondary display + * does not get canceled. + */ +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, WhenDropKeyEvent_OnlyCancelCorrespondingKeyGesture) { + // Send a key down on primary display + mDispatcher->notifyKey( + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .displayId(ADISPLAY_ID_DEFAULT) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build()); + windowInPrimary->consumeKeyEvent( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT))); + windowInSecondary->assertNoEvents(); + + // Send a key down on second display + mDispatcher->notifyKey( + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .displayId(SECOND_DISPLAY_ID) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build()); + windowInSecondary->consumeKeyEvent( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithDisplayId(SECOND_DISPLAY_ID))); + windowInPrimary->assertNoEvents(); + + // Send a valid key up event on primary display that will be dropped because it is stale + NotifyKeyArgs staleKeyUp = + KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD) + .displayId(ADISPLAY_ID_DEFAULT) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build(); + static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 10ms; + mFakePolicy->setStaleEventTimeout(STALE_EVENT_TIMEOUT); + std::this_thread::sleep_for(STALE_EVENT_TIMEOUT); + mDispatcher->notifyKey(staleKeyUp); + + // Only the key gesture corresponding to the dropped event should receive the cancel event. + // Therefore, windowInPrimary should get the cancel event and windowInSecondary should not + // receive any events. + windowInPrimary->consumeKeyEvent(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), + WithDisplayId(ADISPLAY_ID_DEFAULT), + WithFlags(AKEY_EVENT_FLAG_CANCELED))); + windowInSecondary->assertNoEvents(); +} + +/** + * Similar to 'WhenDropKeyEvent_OnlyCancelCorrespondingKeyGesture' but for motion events. + */ +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, WhenDropMotionEvent_OnlyCancelCorrespondingGesture) { + // Send touch down on primary display. + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200)) + .displayId(ADISPLAY_ID_DEFAULT) + .build()); + windowInPrimary->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT))); + windowInSecondary->assertNoEvents(); + + // Send touch down on second display. + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200)) + .displayId(SECOND_DISPLAY_ID) + .build()); + windowInPrimary->assertNoEvents(); + windowInSecondary->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(SECOND_DISPLAY_ID))); + + // inject a valid MotionEvent on primary display that will be stale when it arrives. + NotifyMotionArgs staleMotionUp = + MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200)) + .build(); + static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 10ms; + mFakePolicy->setStaleEventTimeout(STALE_EVENT_TIMEOUT); + std::this_thread::sleep_for(STALE_EVENT_TIMEOUT); + mDispatcher->notifyMotion(staleMotionUp); + + // For stale motion events, we let the gesture to complete. This behaviour is different from key + // events, where we would cancel the current keys instead. + windowInPrimary->consumeMotionEvent(WithMotionAction(ACTION_UP)); + windowInSecondary->assertNoEvents(); +} + class InputFilterTest : public InputDispatcherTest { protected: void testNotifyMotion(int32_t displayId, bool expectToBeFiltered, @@ -7407,6 +8071,130 @@ TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDevic /*resolvedDeviceId=*/VIRTUAL_KEYBOARD_ID, /*flags=*/0); } +class InputDispatcherUserActivityPokeTests : public InputDispatcherTest { +protected: + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + std::shared_ptr<FakeApplicationHandle> application = + std::make_shared<FakeApplicationHandle>(); + application->setDispatchingTimeout(100ms); + mWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", + ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 100, 100)); + mWindow->setDispatchingTimeout(100ms); + mWindow->setFocusable(true); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); + setFocusedWindow(mWindow); + mWindow->consumeFocusEvent(true); + } + + void notifyAndConsumeMotion(int32_t action, uint32_t source, int32_t displayId, + nsecs_t eventTime) { + mDispatcher->notifyMotion(MotionArgsBuilder(action, source) + .displayId(displayId) + .eventTime(eventTime) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + mWindow->consumeMotionEvent(WithMotionAction(action)); + } + +private: + sp<FakeWindowHandle> mWindow; +}; + +TEST_F_WITH_FLAGS( + InputDispatcherUserActivityPokeTests, MinPokeTimeObserved, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rate_limit_user_activity_poke_in_dispatcher))) { + mDispatcher->setMinTimeBetweenUserActivityPokes(50ms); + + // First event of type TOUCH. Should poke. + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(50)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); + + // 80ns > 50ns has passed since previous TOUCH event. Should poke. + notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(130)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); + + // First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event). + notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(135)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}}); + + // Within 50ns of previous TOUCH event. Should NOT poke. + notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(140)); + mFakePolicy->assertUserActivityNotPoked(); + + // Within 50ns of previous OTHER event. Should NOT poke. + notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(150)); + mFakePolicy->assertUserActivityNotPoked(); + + // Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke. + // Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source. + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(160)); + mFakePolicy->assertUserActivityNotPoked(); + + // 65ns > 50ns has passed since previous OTHER event. Should poke. + notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(200)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}}); + + // 170ns > 50ns has passed since previous TOUCH event. Should poke. + notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(300)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); + + // Assert that there's no more user activity poke event. + mFakePolicy->assertUserActivityNotPoked(); +} + +TEST_F_WITH_FLAGS( + InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rate_limit_user_activity_poke_in_dispatcher))) { + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(200)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); + + notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(280)); + mFakePolicy->assertUserActivityNotPoked(); + + notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(340)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); +} + +TEST_F_WITH_FLAGS( + InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rate_limit_user_activity_poke_in_dispatcher))) { + mDispatcher->setMinTimeBetweenUserActivityPokes(0ms); + + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20); + mFakePolicy->assertUserActivityPoked(); + + notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 30); + mFakePolicy->assertUserActivityPoked(); +} + class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); @@ -7530,8 +8318,7 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { ADISPLAY_ID_DEFAULT); mWindow1->setFrame(Rect(0, 0, 100, 100)); - mWindow2 = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window 2", - ADISPLAY_ID_DEFAULT, mWindow1->getToken()); + mWindow2 = mWindow1->clone(ADISPLAY_ID_DEFAULT); mWindow2->setFrame(Rect(100, 100, 200, 200)); mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0}); @@ -7550,12 +8337,9 @@ protected: void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction, const std::vector<PointF>& points) { const std::string name = window->getName(); - MotionEvent* motionEvent = window->consumeMotion(); - - ASSERT_NE(nullptr, motionEvent) - << name.c_str() << ": consumer should have returned non-NULL event."; - - ASSERT_THAT(*motionEvent, WithMotionAction(expectedAction)); + std::unique_ptr<MotionEvent> motionEvent = + window->consumeMotionEvent(WithMotionAction(expectedAction)); + ASSERT_NE(nullptr, motionEvent); ASSERT_EQ(points.size(), motionEvent->getPointerCount()); for (size_t i = 0; i < points.size(); i++) { @@ -7571,13 +8355,13 @@ protected: } } - void touchAndAssertPositions(int32_t action, const std::vector<PointF>& touchedPoints, + void touchAndAssertPositions(sp<FakeWindowHandle> touchedWindow, int32_t action, + const std::vector<PointF>& touchedPoints, std::vector<PointF> expectedPoints) { mDispatcher->notifyMotion(generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints)); - // Always consume from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, action, expectedPoints); + consumeMotionEvent(touchedWindow, action, expectedPoints); } }; @@ -7585,15 +8369,15 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) { // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); // Release touch on Window 1 - touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); } TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) { @@ -7604,21 +8388,21 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); // Release touch on Window 1 - touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); - touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); // Update the transform so rotation is set mWindow2->setWindowTransform(0, -1, 1, 0); mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0}); expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) { @@ -7628,22 +8412,25 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 + // Since this is part of the same touch gesture that has already been dispatched to Window 1, + // the touch stream from Window 2 will be merged with the stream in Window 1. The merged stream + // will continue to be dispatched through Window 1. touchedPoints.push_back(PointF{150, 150}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); // Release Window 2 - touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_UP, touchedPoints, expectedPoints); expectedPoints.pop_back(); // Update the transform so rotation is set for Window 2 mWindow2->setWindowTransform(0, -1, 1, 0); mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) { @@ -7653,37 +8440,37 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTrans // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 touchedPoints.push_back(PointF{150, 150}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); // Release Window 2 - touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_UP, touchedPoints, expectedPoints); expectedPoints.pop_back(); // Touch Window 2 mWindow2->setWindowTransform(0, -1, 1, 0); mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { @@ -7693,20 +8480,20 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 touchedPoints.push_back(PointF{150, 150}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); } /** @@ -7750,7 +8537,7 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, HoverIntoClone) { .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150)) .build()); consumeMotionEvent(mWindow1, ACTION_HOVER_EXIT, {{50, 50}}); - consumeMotionEvent(mWindow1, ACTION_HOVER_ENTER, + consumeMotionEvent(mWindow2, ACTION_HOVER_ENTER, {getPointInWindow(mWindow2->getInfo(), PointF{150, 150})}); } @@ -7855,7 +8642,7 @@ TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); - std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN + const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow); @@ -7871,7 +8658,7 @@ TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { // Inject a key, and don't respond - expect that ANR is called. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher)); - std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); + const auto [sequenceNum, _] = mWindow->receiveEvent(); ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow); @@ -8031,7 +8818,7 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowAnr) { WINDOW_LOCATION)); mWindow->consumeMotionDown(); - std::optional<uint32_t> sequenceNum = spy->receiveEvent(); // ACTION_DOWN + const auto [sequenceNum, _] = spy->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = spy->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, spy); @@ -8172,41 +8959,31 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) { /** * If a window is processing a motion event, and then a key event comes in, the key event should * not get delivered to the focused window until the motion is processed. - * - * Warning!!! - * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT - * and the injection timeout that we specify when injecting the key. - * We must have the injection timeout (100ms) be smaller than - * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). - * - * If that value changes, this test should also change. */ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { + // The timeouts in this test are established by relying on the fact that the "key waiting for + // events timeout" is equal to 500ms. + ASSERT_EQ(mFakePolicy->getKeyWaitingForEventsTimeout(), 500ms); mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); tapOnWindow(); - std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent(); + const auto& [downSequenceNum, downEvent] = mWindow->receiveEvent(); ASSERT_TRUE(downSequenceNum); - std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent(); + const auto& [upSequenceNum, upEvent] = mWindow->receiveEvent(); ASSERT_TRUE(upSequenceNum); - // Don't finish the events yet, and send a key - // Injection will "succeed" because we will eventually give up and send the key to the focused - // window even if motions are still being processed. But because the injection timeout is short, - // we will receive INJECTION_TIMED_OUT as the result. - InputEventInjectionResult result = - injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT, - InputEventInjectionSync::WAIT_FOR_RESULT, 100ms); - ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); + // Don't finish the events yet, and send a key + mDispatcher->notifyKey( + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build()); // Key will not be sent to the window, yet, because the window is still processing events // and the key remains pending, waiting for the touch events to be processed // Make sure that `assertNoEvents` doesn't wait too long, because it could cause an ANR. - // Rely here on the fact that it uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood. - static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms); - mWindow->assertNoEvents(); + mWindow->assertNoEvents(100ms); - std::this_thread::sleep_for(500ms); + std::this_thread::sleep_for(400ms); // if we wait long enough though, dispatcher will give up, and still send the key // to the focused window, even though we have not yet finished the motion event mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); @@ -8221,14 +8998,17 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { * focused window right away. */ TEST_F(InputDispatcherSingleWindowAnr, - PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) { + PendingKey_IsDeliveredWhileMotionIsProcessingAndNewTouchComesIn) { + // The timeouts in this test are established by relying on the fact that the "key waiting for + // events timeout" is equal to 500ms. + ASSERT_EQ(mFakePolicy->getKeyWaitingForEventsTimeout(), 500ms); mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); tapOnWindow(); - std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent(); + const auto& [downSequenceNum, _] = mWindow->receiveEvent(); ASSERT_TRUE(downSequenceNum); - std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent(); + const auto& [upSequenceNum, upEvent] = mWindow->receiveEvent(); ASSERT_TRUE(upSequenceNum); // Don't finish the events yet, and send a key mDispatcher->notifyKey( @@ -8236,15 +9016,19 @@ TEST_F(InputDispatcherSingleWindowAnr, .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) .build()); // At this point, key is still pending, and should not be sent to the application yet. - // Make sure the `assertNoEvents` check doesn't take too long. It uses - // CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood. - static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms); - mWindow->assertNoEvents(); + mWindow->assertNoEvents(100ms); // Now tap down again. It should cause the pending key to go to the focused window right away. tapOnWindow(); - mWindow->consumeKeyEvent(WithKeyAction(AKEY_EVENT_ACTION_DOWN)); // it doesn't matter that we - // haven't ack'd the other events yet. We can finish events in any order. + // Now that we tapped, we should receive the key immediately. + // Since there's still room for slowness, we use 200ms, which is much less than + // the "key waiting for events' timeout of 500ms minus the already waited 100ms duration. + std::unique_ptr<InputEvent> keyEvent = mWindow->consume(200ms); + ASSERT_NE(nullptr, keyEvent); + ASSERT_EQ(InputEventType::KEY, keyEvent->getType()); + ASSERT_THAT(static_cast<KeyEvent&>(*keyEvent), WithKeyAction(AKEY_EVENT_ACTION_DOWN)); + // it doesn't matter that we haven't ack'd the other events yet. We can finish events in any + // order. mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP mWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); @@ -8264,7 +9048,7 @@ TEST_F(InputDispatcherSingleWindowAnr, TwoGesturesWithAnr) { .pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10)) .build()); - std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN + const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow); @@ -8301,6 +9085,61 @@ TEST_F(InputDispatcherSingleWindowAnr, TwoGesturesWithAnr) { mWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); } +// Send an event to the app and have the app not respond right away. Then remove the app window. +// When the window is removed, the dispatcher will cancel the events for that window. +// So InputDispatcher will enqueue ACTION_CANCEL event as well. +TEST_F(InputDispatcherSingleWindowAnr, AnrAfterWindowRemoval) { + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {WINDOW_LOCATION})); + + const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + + // Remove the window, but the input channel should remain alive. + mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); + + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + // Since the window was removed, Dispatcher does not know the PID associated with the window + // anymore, so the policy is notified without the PID. + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken(), + /*pid=*/std::nullopt); + + mWindow->finishEvent(*sequenceNum); + // The cancellation was generated when the window was removed, along with the focus event. + mWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT))); + mWindow->consumeFocusEvent(false); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), /*pid=*/std::nullopt); +} + +// Send an event to the app and have the app not respond right away. Wait for the policy to be +// notified of the unresponsive window, then remove the app window. +TEST_F(InputDispatcherSingleWindowAnr, AnrFollowedByWindowRemoval) { + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {WINDOW_LOCATION})); + + const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow); + + // Remove the window, but the input channel should remain alive. + mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); + + mWindow->finishEvent(*sequenceNum); + // The cancellation was generated during the ANR, and the window lost focus when it was removed. + mWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT))); + mWindow->consumeFocusEvent(false); + ASSERT_TRUE(mDispatcher->waitForIdle()); + // Since the window was removed, Dispatcher does not know the PID associated with the window + // becoming responsive, so the policy is notified without the PID. + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), /*pid=*/std::nullopt); +} + class InputDispatcherMultiWindowAnr : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); @@ -8380,8 +9219,7 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { .build())); mFocusedWindow->consumeMotionDown(); mFocusedWindow->consumeMotionUp(); - mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, - ADISPLAY_ID_DEFAULT, /*flags=*/0); + mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0); // We consumed all events, so no ANR ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertNotifyAnrWasNotCalled(); @@ -8394,7 +9232,7 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { .x(FOCUSED_WINDOW_LOCATION.x) .y(FOCUSED_WINDOW_LOCATION.y)) .build())); - std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent(); + const auto [unfocusedSequenceNum, _] = mUnfocusedWindow->receiveEvent(); ASSERT_TRUE(unfocusedSequenceNum); const std::chrono::duration timeout = @@ -8457,12 +9295,11 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout // At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events. TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { tapOnFocusedWindow(); - mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, - ADISPLAY_ID_DEFAULT, /*flags=*/0); + mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0); // Receive the events, but don't respond - std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN + const auto [downEventSequenceNum, downEvent] = mFocusedWindow->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(downEventSequenceNum); - std::optional<uint32_t> upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP + const auto [upEventSequenceNum, upEvent] = mFocusedWindow->receiveEvent(); // ACTION_UP ASSERT_TRUE(upEventSequenceNum); const std::chrono::duration timeout = mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); @@ -8540,9 +9377,9 @@ TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) { {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0}); tapOnUnfocusedWindow(); - std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent(); + const auto [downSequenceNum, downEvent] = mUnfocusedWindow->receiveEvent(); ASSERT_TRUE(downSequenceNum); - std::optional<uint32_t> upSequenceNum = mUnfocusedWindow->receiveEvent(); + const auto [upSequenceNum, upEvent] = mUnfocusedWindow->receiveEvent(); ASSERT_TRUE(upSequenceNum); // Don't finish the events yet, and send a key // Injection will succeed because we will eventually give up and send the key to the focused @@ -8590,8 +9427,7 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION})); - mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, - ADISPLAY_ID_DEFAULT, /*flags=*/0); + mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0); // Touch Window 2 mDispatcher->notifyMotion( @@ -8606,8 +9442,7 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { mFocusedWindow->consumeMotionDown(); // Focused window may or may not receive ACTION_MOVE // But it should definitely receive ACTION_CANCEL due to the ANR - InputEvent* event; - std::optional<int32_t> moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event); + const auto [moveOrCancelSequenceNum, event] = mFocusedWindow->receiveEvent(); ASSERT_TRUE(moveOrCancelSequenceNum); mFocusedWindow->finishEvent(*moveOrCancelSequenceNum); ASSERT_NE(nullptr, event); @@ -8691,6 +9526,104 @@ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled()); } +/** + * If we are pruning input queue, we should never drop pointer events. Otherwise, we risk having + * an inconsistent event stream inside the dispatcher. In this test, we make sure that the + * dispatcher doesn't prune pointer events incorrectly. + * + * This test reproduces a crash in InputDispatcher. + * To reproduce the crash, we need to simulate the conditions for "pruning input queue" to occur. + * + * Keep the currently focused application (mApplication), and have no focused window. + * We set up two additional windows: + * 1) The navigation bar window. This simulates the system "NavigationBar", which is used in the + * 3-button navigation mode. This window injects a BACK button when it's touched. 2) The application + * window. This window is not focusable, but is touchable. + * + * We first touch the navigation bar, which causes it to inject a key. Since there's no focused + * window, the dispatcher doesn't process this key, and all other events inside dispatcher are now + * blocked. The dispatcher is waiting for 'mApplication' to add a focused window. + * + * Now, we touch "Another window". This window is owned by a different application than + * 'mApplication'. This causes the dispatcher to stop waiting for 'mApplication' to add a focused + * window. Now, the "pruning input queue" behaviour should kick in, and the dispatcher should start + * dropping the events from its queue. Ensure that no crash occurs. + * + * In this test, we are setting long timeouts to prevent ANRs and events dropped due to being stale. + * This does not affect the test running time. + */ +TEST_F(InputDispatcherMultiWindowAnr, PruningInputQueueShouldNotDropPointerEvents) { + std::shared_ptr<FakeApplicationHandle> systemUiApplication = + std::make_shared<FakeApplicationHandle>(); + systemUiApplication->setDispatchingTimeout(3000ms); + mFakePolicy->setStaleEventTimeout(3000ms); + sp<FakeWindowHandle> navigationBar = + sp<FakeWindowHandle>::make(systemUiApplication, mDispatcher, "NavigationBar", + ADISPLAY_ID_DEFAULT); + navigationBar->setFocusable(false); + navigationBar->setWatchOutsideTouch(true); + navigationBar->setFrame(Rect(0, 0, 100, 100)); + + mApplication->setDispatchingTimeout(3000ms); + // 'mApplication' is already focused, but we call it again here to make it explicit. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + + std::shared_ptr<FakeApplicationHandle> anotherApplication = + std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> appWindow = + sp<FakeWindowHandle>::make(anotherApplication, mDispatcher, "Another window", + ADISPLAY_ID_DEFAULT); + appWindow->setFocusable(false); + appWindow->setFrame(Rect(100, 100, 200, 200)); + + mDispatcher->onWindowInfosChanged( + {{*navigationBar->getInfo(), *appWindow->getInfo()}, {}, 0, 0}); + // 'mFocusedWindow' is no longer in the dispatcher window list, and therefore loses focus + mFocusedWindow->consumeFocusEvent(false); + + // Touch down the navigation bar. It consumes the touch and injects a key into the dispatcher + // in response. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + navigationBar->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Key will not be sent anywhere because we have no focused window. It will remain pending. + // Pretend we are injecting KEYCODE_BACK, but it doesn't actually matter what key it is. + InputEventInjectionResult result = + injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT, + InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms, + /*allowKeyRepeat=*/false); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); + + // Finish the gesture - lift up finger and inject ACTION_UP key event + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + result = injectKey(*mDispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT, + InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms, + /*allowKeyRepeat=*/false); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); + // The key that was injected is blocking the dispatcher, so the navigation bar shouldn't be + // getting any events yet. + navigationBar->assertNoEvents(); + + // Now touch "Another window". This touch is going to a different application than the one we + // are waiting for (which is 'mApplication'). + // This should cause the dispatcher to drop the pending focus-dispatched events (like the key + // trying to be injected) and to continue processing the rest of the events in the original + // order. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150)) + .build()); + navigationBar->consumeMotionEvent(WithMotionAction(ACTION_UP)); + navigationBar->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE)); + appWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + appWindow->assertNoEvents(); + navigationBar->assertNoEvents(); +} + // These tests ensure we cannot send touch events to a window that's positioned behind a window // that has feature NO_INPUT_CHANNEL. // Layout: @@ -8706,7 +9639,7 @@ class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest { mNoInputWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Window without input channel", ADISPLAY_ID_DEFAULT, - /*token=*/std::make_optional<sp<IBinder>>(nullptr)); + /*createInputChannel=*/false); mNoInputWindow->setNoInputChannel(true); mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); // It's perfectly valid for this window to not have an associated input channel @@ -8774,8 +9707,7 @@ protected: InputDispatcherTest::SetUp(); mApp = std::make_shared<FakeApplicationHandle>(); mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); - mMirror = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindowMirror", - ADISPLAY_ID_DEFAULT, mWindow->getToken()); + mMirror = mWindow->clone(ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); mWindow->setFocusable(true); mMirror->setFocusable(true); @@ -8799,7 +9731,7 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, CanGetFocus) { TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) { setFocusedWindow(mMirror); - // window gets focused + // window gets focused because it is above the mirror mWindow->consumeFocusEvent(true); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; @@ -8872,10 +9804,10 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedWhileWindowsAlive) { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; - mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + mMirror->consumeKeyDown(ADISPLAY_ID_NONE); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; - mWindow->consumeKeyUp(ADISPLAY_ID_NONE); + mMirror->consumeKeyUp(ADISPLAY_ID_NONE); // Both windows are removed mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); @@ -9599,11 +10531,11 @@ protected: // Transfer touch focus to the drag window bool transferred = - mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(), - /*isDragDrop=*/true); + mDispatcher->transferTouchGesture(mWindow->getToken(), mDragWindow->getToken(), + /*isDragDrop=*/true); if (transferred) { mWindow->consumeMotionCancel(); - mDragWindow->consumeMotionDown(); + mDragWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } return transferred; } @@ -9617,7 +10549,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->assertNoEvents(); @@ -9626,7 +10558,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(true, 150, 50); mSecondWindow->consumeDragEvent(false, 50, 50); @@ -9635,7 +10567,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->consumeDragEvent(true, -50, 50); @@ -9643,7 +10575,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); } @@ -9680,7 +10612,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->assertNoEvents(); @@ -9689,7 +10621,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(true, 150, 50); mSecondWindow->consumeDragEvent(false, 50, 50); @@ -9698,7 +10630,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) { injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken()); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); @@ -9723,7 +10655,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropNotCancelledIfSomeOtherPointerIsPilf // Receives cancel for first pointer after next pointer down mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); - mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + mSpyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithPointerIds({1}))); mDragWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); mSpyWindow->assertNoEvents(); @@ -9733,18 +10665,19 @@ TEST_F(InputDispatcherDragTests, DragAndDropNotCancelledIfSomeOtherPointerIsPilf mDragWindow->assertNoEvents(); const MotionEvent firstFingerMoveEvent = - MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(60).y(60)) .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60)) .build(); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + injectMotionEvent(*mDispatcher, firstFingerMoveEvent, INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Drag window should still receive the new event - mDragWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + mDragWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))); mDragWindow->assertNoEvents(); } @@ -9759,7 +10692,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50)) .build())) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->assertNoEvents(); @@ -9771,7 +10704,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { .pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50)) .build())) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken()); @@ -9784,7 +10717,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { .pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50)) .build())) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); } @@ -9802,7 +10735,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->assertNoEvents(); @@ -9811,7 +10744,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(true, 150, 50); mSecondWindow->assertNoEvents(); @@ -9820,7 +10753,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) { injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mFakePolicy->assertDropTargetEquals(*mDispatcher, nullptr); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); @@ -9875,6 +10808,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) { InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + mSecondWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); // Perform drag and drop from first window. ASSERT_TRUE(startDrag(false)); @@ -9889,7 +10823,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)); - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->consumeMotionMove(); @@ -9903,7 +10837,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)); - mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken()); mWindow->assertNoEvents(); mSecondWindow->consumeMotionMove(); @@ -9930,8 +10864,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) { .displayId(SECOND_DISPLAY_ID) .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100)) .build())); - windowInSecondary->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN, - SECOND_DISPLAY_ID, /*expectedFlag=*/0); + windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID, /*expectedFlag=*/0); // Update window again. mDispatcher->onWindowInfosChanged( {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(), @@ -9945,7 +10878,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->assertNoEvents(); @@ -9954,7 +10887,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) { injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(true, 150, 50); mSecondWindow->consumeDragEvent(false, 50, 50); @@ -9963,7 +10896,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) { injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken()); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); @@ -9981,7 +10914,7 @@ TEST_F(InputDispatcherDragTests, MouseDragAndDrop) { .y(50)) .build())) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->assertNoEvents(); @@ -9995,7 +10928,7 @@ TEST_F(InputDispatcherDragTests, MouseDragAndDrop) { .y(50)) .build())) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(true, 150, 50); mSecondWindow->consumeDragEvent(false, 50, 50); @@ -10009,7 +10942,7 @@ TEST_F(InputDispatcherDragTests, MouseDragAndDrop) { .y(50)) .build())) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken()); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); @@ -10050,7 +10983,8 @@ TEST_F(InputDispatcherDragTests, DragAndDropFinishedWhenCancelCurrentTouch) { // Trigger cancel mDispatcher->cancelCurrentTouch(); ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionCancel()); - ASSERT_NO_FATAL_FAILURE(mDragWindow->consumeMotionCancel()); + ASSERT_NO_FATAL_FAILURE(mDragWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)); ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionCancel()); ASSERT_TRUE(mDispatcher->waitForIdle()); diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index 36be684f96..2aecab98fd 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -19,12 +19,16 @@ #include <InputReaderBase.h> #include <gtest/gtest.h> #include <ui/Rotation.h> +#include <utils/Timers.h> + +#include "NotifyArgs.h" namespace android { +using testing::_; using testing::Return; -void InputMapperUnitTest::SetUp() { +void InputMapperUnitTest::SetUpWithBus(int bus) { mFakePointerController = std::make_shared<FakePointerController>(); mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); mFakePointerController->setPosition(INITIAL_CURSOR_X, INITIAL_CURSOR_Y); @@ -36,20 +40,30 @@ void InputMapperUnitTest::SetUp() { EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get())); EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub)); - InputDeviceIdentifier identifier; - identifier.name = "device"; - identifier.location = "USB1"; - identifier.bus = 0; - EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID)).WillRepeatedly(Return(identifier)); + mIdentifier.name = "device"; + mIdentifier.location = "USB1"; + mIdentifier.bus = bus; + EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID)) + .WillRepeatedly(Return(mIdentifier)); + EXPECT_CALL(mMockEventHub, getConfiguration(EVENTHUB_ID)).WillRepeatedly([&](int32_t) { + return mPropertyMap; + }); +} + +void InputMapperUnitTest::createDevice() { mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID, - /*generation=*/2, identifier); + /*generation=*/2, mIdentifier); + mDevice->addEmptyEventHubDevice(EVENTHUB_ID); mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID); + std::list<NotifyArgs> args = + mDevice->configure(systemTime(), mReaderConfiguration, /*changes=*/{}); + ASSERT_THAT(args, testing::ElementsAre(testing::VariantWith<NotifyDeviceResetArgs>(_))); } void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution) { - EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_)) + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, _)) .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) { outAxisInfo->valid = valid; outAxisInfo->minValue = min; @@ -215,8 +229,8 @@ std::list<NotifyArgs> InputMapperTest::handleTimeout(InputMapper& mapper, nsecs_ return generatedArgs; } -void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, - float min, float max, float flat, float fuzz) { +void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min, + float max, float flat, float fuzz) { const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source); ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source; ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source; @@ -227,11 +241,9 @@ void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axi ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source; } -void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y, - float pressure, float size, float touchMajor, - float touchMinor, float toolMajor, float toolMinor, - float orientation, float distance, - float scaledAxisEpsilon) { +void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, float size, + float touchMajor, float touchMinor, float toolMajor, float toolMinor, + float orientation, float distance, float scaledAxisEpsilon) { ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon); ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon); ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON); diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index 05b0e97ff6..e176a65551 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -32,6 +32,7 @@ #include "InterfaceMocks.h" #include "TestConstants.h" #include "TestInputListener.h" +#include "input/PropertyMap.h" namespace android { @@ -41,7 +42,15 @@ protected: static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; static constexpr float INITIAL_CURSOR_X = 400; static constexpr float INITIAL_CURSOR_Y = 240; - virtual void SetUp() override; + virtual void SetUp() override { SetUpWithBus(0); } + virtual void SetUpWithBus(int bus); + + /** + * Initializes mDevice and mDeviceContext. When this happens, mDevice takes a copy of + * mPropertyMap, so tests that need to set configuration properties should do so before calling + * this. Others will most likely want to call it in their SetUp method. + */ + void createDevice(); void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution); @@ -54,6 +63,7 @@ protected: std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value); std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value); + InputDeviceIdentifier mIdentifier; MockEventHubInterface mMockEventHub; sp<FakeInputReaderPolicy> mFakePolicy; std::shared_ptr<FakePointerController> mFakePointerController; @@ -64,6 +74,7 @@ protected: InputReaderConfiguration mReaderConfiguration; // The mapper should be created by the subclasses. std::unique_ptr<InputMapper> mMapper; + PropertyMap mPropertyMap; }; /** @@ -130,13 +141,13 @@ protected: void resetMapper(InputMapper& mapper, nsecs_t when); std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when); - - static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, - float min, float max, float flat, float fuzz); - static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, - float size, float touchMajor, float touchMinor, float toolMajor, - float toolMinor, float orientation, float distance, - float scaledAxisEpsilon = 1.f); }; +void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min, + float max, float flat, float fuzz); + +void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, float size, + float touchMajor, float touchMinor, float toolMajor, float toolMinor, + float orientation, float distance, float scaledAxisEpsilon = 1.f); + } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index e0a3e94aad..835f8b89c3 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -58,6 +58,7 @@ namespace android { using namespace ftl::flag_operators; using testing::AllOf; using std::chrono_literals::operator""ms; +using std::chrono_literals::operator""s; // Arbitrary display properties. static constexpr int32_t DISPLAY_ID = 0; @@ -97,8 +98,6 @@ static constexpr uint32_t STYLUS_FUSION_SOURCE = // Minimum timestamp separation between subsequent input events from a Bluetooth device. static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); -// Maximum smoothing time delta so that we don't generate events too far into the future. -constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); namespace input_flags = com::android::input::flags; @@ -151,7 +150,7 @@ static void assertAxisNotPresent(MultiTouchInputMapper& mapper, int axis) { std::istringstream iss(dump); for (std::string line; std::getline(iss, line);) { ALOGE("%s", line.c_str()); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + std::this_thread::sleep_for(1ms); } } @@ -1352,6 +1351,9 @@ protected: std::shared_ptr<FakePointerController> mFakePointerController; + constexpr static auto EVENT_HAPPENED_TIMEOUT = 2000ms; + constexpr static auto EVENT_DID_NOT_HAPPEN_TIMEOUT = 30ms; + void SetUp() override { #if !defined(__ANDROID__) GTEST_SKIP(); @@ -1373,18 +1375,28 @@ protected: mFakePolicy.clear(); } - std::optional<InputDeviceInfo> findDeviceByName(const std::string& name) { - const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices(); - const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(), - [&name](const InputDeviceInfo& info) { - return info.getIdentifier().name == name; - }); - return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt; + std::optional<InputDeviceInfo> waitForDevice(const std::string& deviceName) { + std::chrono::time_point start = std::chrono::steady_clock::now(); + while (true) { + const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices(); + const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(), + [&deviceName](const InputDeviceInfo& info) { + return info.getIdentifier().name == deviceName; + }); + if (it != inputDevices.end()) { + return std::make_optional(*it); + } + std::this_thread::sleep_for(1ms); + std::chrono::duration elapsed = std::chrono::steady_clock::now() - start; + if (elapsed > 5s) { + return {}; + } + } } void setupInputReader() { - mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms, - /*eventDidNotHappenTimeout=*/30ms); + mTestListener = std::make_unique<TestInputListener>(EVENT_HAPPENED_TIMEOUT, + EVENT_DID_NOT_HAPPEN_TIMEOUT); mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy, *mTestListener); @@ -1432,7 +1444,7 @@ TEST_F(InputReaderIntegrationTest, AddNewDevice) { ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size()); - const auto device = findDeviceByName(keyboard->getName()); + const auto device = waitForDevice(keyboard->getName()); ASSERT_TRUE(device.has_value()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources()); @@ -1475,7 +1487,7 @@ TEST_F(InputReaderIntegrationTest, ExternalStylusesButtons) { std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - const auto device = findDeviceByName(stylus->getName()); + const auto device = waitForDevice(stylus->getName()); ASSERT_TRUE(device.has_value()); // An external stylus with buttons should also be recognized as a keyboard. @@ -1515,7 +1527,7 @@ TEST_F(InputReaderIntegrationTest, KeyboardWithStylusButtons) { BTN_STYLUS3}); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - const auto device = findDeviceByName(keyboard->getName()); + const auto device = waitForDevice(keyboard->getName()); ASSERT_TRUE(device.has_value()); // An alphabetical keyboard that reports stylus buttons should not be recognized as a stylus. @@ -1532,7 +1544,7 @@ TEST_F(InputReaderIntegrationTest, HidUsageKeyboardIsNotAStylus) { std::initializer_list<int>{KEY_VOLUMEUP, KEY_VOLUMEDOWN}); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - const auto device = findDeviceByName(keyboard->getName()); + const auto device = waitForDevice(keyboard->getName()); ASSERT_TRUE(device.has_value()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources()) @@ -1586,7 +1598,7 @@ protected: mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); - const auto info = findDeviceByName(mDevice->getName()); + const auto info = waitForDevice(mDevice->getName()); ASSERT_TRUE(info); mDeviceInfo = *info; } @@ -1657,7 +1669,7 @@ protected: ViewportType::INTERNAL); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); - const auto info = findDeviceByName(mDevice->getName()); + const auto info = waitForDevice(mDevice->getName()); ASSERT_TRUE(info); mDeviceInfo = *info; } @@ -1990,7 +2002,7 @@ TEST_P(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) { auto externalStylus = createUinputDevice<UinputExternalStylus>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); - const auto stylusInfo = findDeviceByName(externalStylus->getName()); + const auto stylusInfo = waitForDevice(externalStylus->getName()); ASSERT_TRUE(stylusInfo); // Move @@ -2061,7 +2073,7 @@ private: mStylus = mStylusDeviceLifecycleTracker.get(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); - const auto info = findDeviceByName(mStylus->getName()); + const auto info = waitForDevice(mStylus->getName()); ASSERT_TRUE(info); mStylusInfo = *info; } @@ -2331,11 +2343,11 @@ TEST_F(ExternalStylusIntegrationTest, ExternalStylusConnectionChangesTouchscreen createUinputDevice<UinputExternalStylusWithPressure>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); - const auto stylusInfo = findDeviceByName(stylus->getName()); + const auto stylusInfo = waitForDevice(stylus->getName()); ASSERT_TRUE(stylusInfo); // Connecting an external stylus changes the source of the touchscreen. - const auto deviceInfo = findDeviceByName(mDevice->getName()); + const auto deviceInfo = waitForDevice(mDevice->getName()); ASSERT_TRUE(deviceInfo); ASSERT_TRUE(isFromSource(deviceInfo->getSources(), STYLUS_FUSION_SOURCE)); } @@ -2349,7 +2361,7 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) { createUinputDevice<UinputExternalStylusWithPressure>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); - const auto stylusInfo = findDeviceByName(stylus->getName()); + const auto stylusInfo = waitForDevice(stylus->getName()); ASSERT_TRUE(stylusInfo); ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); @@ -2395,7 +2407,7 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { createUinputDevice<UinputExternalStylusWithPressure>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); - const auto stylusInfo = findDeviceByName(stylus->getName()); + const auto stylusInfo = waitForDevice(stylus->getName()); ASSERT_TRUE(stylusInfo); ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); @@ -2415,17 +2427,29 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { mDevice->sendTrackingId(FIRST_TRACKING_ID); mDevice->sendToolType(MT_TOOL_FINGER); mDevice->sendDown(centerPoint); - auto waitUntil = std::chrono::system_clock::now() + - std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT)); + const auto syncTime = std::chrono::system_clock::now(); + // After 72 ms, the event *will* be generated. If we wait the full 72 ms to check that NO event + // is generated in that period, there will be a race condition between the event being generated + // and the test's wait timeout expiring. Thus, we wait for a shorter duration in the test, which + // will reduce the liklihood of the race condition occurring. + const auto waitUntilTimeForNoEvent = + syncTime + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT / 2)); mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntil)); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntilTimeForNoEvent)); // Since the external stylus did not report a pressure value within the timeout, // it shows up as a finger pointer. - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS), - WithToolType(ToolType::FINGER), WithDeviceId(touchscreenId), WithPressure(1.f)))); + const auto waitUntilTimeForEvent = syncTime + + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT)) + EVENT_HAPPENED_TIMEOUT; + ASSERT_NO_FATAL_FAILURE( + mTestListener->assertNotifyMotionWasCalled(AllOf(WithMotionAction( + AMOTION_EVENT_ACTION_DOWN), + WithSource(AINPUT_SOURCE_TOUCHSCREEN | + AINPUT_SOURCE_STYLUS), + WithToolType(ToolType::FINGER), + WithDeviceId(touchscreenId), + WithPressure(1.f)), + waitUntilTimeForEvent)); // Change the pressure on the external stylus. Since the pressure was not present at the start // of the gesture, it is ignored for now. @@ -2463,7 +2487,7 @@ TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) { std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); - const auto stylusInfo = findDeviceByName(stylus->getName()); + const auto stylusInfo = waitForDevice(stylus->getName()); ASSERT_TRUE(stylusInfo); ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); @@ -2693,6 +2717,31 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled()); } +TEST_F(InputDeviceTest, Configure_SmoothScrollViewBehaviorNotSet) { + // Set some behavior to force the configuration to be update. + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1"); + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + + ASSERT_FALSE(mDevice->getDeviceInfo().getViewBehavior().shouldSmoothScroll.has_value()); +} + +TEST_F(InputDeviceTest, Configure_SmoothScrollViewBehaviorEnabled) { + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.viewBehavior_smoothScroll", "1"); + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + + ASSERT_TRUE(mDevice->getDeviceInfo().getViewBehavior().shouldSmoothScroll.value_or(false)); +} + TEST_F(InputDeviceTest, WakeDevice_AddsWakeFlagToProcessNotifyArgs) { mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1"); FakeInputMapper& mapper = @@ -2855,9 +2904,12 @@ TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) { mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + const auto initialGeneration = mDevice->getGeneration(); unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), InputReaderConfiguration::Change::DISPLAY_INFO); ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId()); + ASSERT_GT(mDevice->getGeneration(), initialGeneration); + ASSERT_EQ(mDevice->getDeviceInfo().getAssociatedDisplayId(), SECONDARY_DISPLAY_ID); } /** @@ -2894,9 +2946,9 @@ TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) { mapper.assertConfigureWasCalled(); mapper.assertResetWasNotCalled(); - RawEvent event{.deviceId = EVENTHUB_ID, + RawEvent event{.when = ARBITRARY_TIME, .readTime = ARBITRARY_TIME, - .when = ARBITRARY_TIME, + .deviceId = EVENTHUB_ID, .type = EV_SYN, .code = SYN_REPORT, .value = 0}; @@ -2906,13 +2958,11 @@ TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) { mapper.assertProcessWasCalled(); // Simulate a kernel buffer overflow, which generates a SYN_DROPPED event. - // This should reset the mapper. event.type = EV_SYN; event.code = SYN_DROPPED; event.value = 0; unused = mDevice->process(&event, /*count=*/1); mapper.assertProcessWasNotCalled(); - mapper.assertResetWasCalled(); // All events until the next SYN_REPORT should be dropped. event.type = EV_KEY; @@ -2922,11 +2972,13 @@ TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) { mapper.assertProcessWasNotCalled(); // We get the SYN_REPORT event now, which is not forwarded to mappers. + // This should reset the mapper. event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; unused = mDevice->process(&event, /*count=*/1); mapper.assertProcessWasNotCalled(); + mapper.assertResetWasCalled(); // The mapper receives events normally now. event.type = EV_KEY; @@ -4087,7 +4139,7 @@ protected: void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); } }; -TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) { +TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) { // For external devices, keys will trigger wake on key down. Media keys should also trigger // wake if triggered from external devices. @@ -4126,6 +4178,36 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) { ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } +TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_NoneAlphabeticKeyboard) { + // For external devices, keys will trigger wake on key down. Media keys should not trigger + // wake if triggered from external non-alphaebtic keyboard (e.g. headsets). + + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, + POLICY_FLAG_WAKE); + + KeyboardInputMapper& mapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1); + NotifyKeyArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); +} + TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { // Tv Remote key's wake behavior is prescribed by the keylayout file. @@ -4164,1492 +4246,6 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } -// --- CursorInputMapperTestBase --- - -class CursorInputMapperTestBase : public InputMapperTest { -protected: - static const int32_t TRACKBALL_MOVEMENT_THRESHOLD; - - std::shared_ptr<FakePointerController> mFakePointerController; - - void SetUp() override { - InputMapperTest::SetUp(); - - mFakePointerController = std::make_shared<FakePointerController>(); - mFakePolicy->setPointerController(mFakePointerController); - } - - void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY, - int32_t rotatedX, int32_t rotatedY); - - void prepareDisplay(ui::Rotation orientation) { - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, - DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); - } - - void prepareSecondaryDisplay() { - setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT, - ViewportType::EXTERNAL); - } - - static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y, - float pressure) { - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(coords, x, y, pressure, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, EPSILON)); - } -}; - -const int32_t CursorInputMapperTestBase::TRACKBALL_MOVEMENT_THRESHOLD = 6; - -void CursorInputMapperTestBase::testMotionRotation(CursorInputMapper& mapper, int32_t originalX, - int32_t originalY, int32_t rotatedX, - int32_t rotatedY) { - NotifyMotionArgs args; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, originalX); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, originalY); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(args.pointerCoords[0], - float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, - float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f)); -} - -// --- CursorInputMapperTest --- - -class CursorInputMapperTest : public CursorInputMapperTestBase { -protected: - void SetUp() override { - input_flags::enable_pointer_choreographer(false); - CursorInputMapperTestBase::SetUp(); - } -}; - -TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); -} - -TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources()); -} - -TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - InputDeviceInfo info; - mapper.populateDeviceInfo(info); - - // Initially there may not be a valid motion range. - ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); - ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, - AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); - - // When the bounds are set, then there should be a valid motion range. - mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - InputDeviceInfo info2; - mapper.populateDeviceInfo(info2); - - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, - AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, - 1, 800 - 1, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, - AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, - 2, 480 - 1, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, - AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, - 0.0f, 1.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - InputDeviceInfo info; - mapper.populateDeviceInfo(info); - - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, - AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL, - -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, - AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL, - -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, - AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TRACKBALL, - 0.0f, 1.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - NotifyMotionArgs args; - - // Button press. - // Mostly testing non x/y behavior here so we don't need to check again elsewhere. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.getPointerCount()); - ASSERT_EQ(0, args.pointerProperties[0].id); - ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.getPointerCount()); - ASSERT_EQ(0, args.pointerProperties[0].id); - ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Button release. Should have same down time. - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, BTN_MOUSE, 0); - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(0, args.buttonState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.getPointerCount()); - ASSERT_EQ(0, args.pointerProperties[0].id); - ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(0, args.buttonState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.getPointerCount()); - ASSERT_EQ(0, args.pointerProperties[0].id); - ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyMotionArgs args; - - // Motion in X but not Y. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, - 0.0f)); - - // Motion in Y but not X. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, - -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f)); -} - -TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyMotionArgs args; - - // Button press. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - // Button release. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyMotionArgs args; - - // Combined X, Y and Button. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); - - // Move X, Y a bit while pressed. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 2); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], - 2.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); - - // Release Button. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) { - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); - addConfigurationProperty("cursor.mode", "navigation"); - // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not - // need to be rotated. - addConfigurationProperty("cursor.orientationAware", "1"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - prepareDisplay(ui::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); -} - -TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) { - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); - addConfigurationProperty("cursor.mode", "navigation"); - // Since InputReader works in the un-rotated coordinate space, only devices that are not - // orientation-aware are affected by display rotation. - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - clearViewports(); - prepareDisplay(ui::ROTATION_0); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); - - clearViewports(); - prepareDisplay(ui::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1)); - - clearViewports(); - prepareDisplay(ui::ROTATION_180); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1)); - - clearViewports(); - prepareDisplay(ui::ROTATION_270); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1)); -} - -TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - - NotifyMotionArgs motionArgs; - NotifyKeyArgs keyArgs; - - // press BTN_LEFT, release BTN_LEFT - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, - motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, - motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - // press BTN_BACK, release BTN_BACK - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - // press BTN_SIDE, release BTN_SIDE - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - // press BTN_FORWARD, release BTN_FORWARD - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - // press BTN_EXTRA, release BTN_EXTRA - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); -} - -TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - - NotifyMotionArgs args; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); -} - -/** - * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any - * pointer acceleration or speed processing should not be applied. - */ -TEST_F(CursorInputMapperTest, PointerCaptureDisablesVelocityProcessing) { - addConfigurationProperty("cursor.mode", "pointer"); - const VelocityControlParameters testParams(/*scale=*/5.f, /*low threshold=*/0.f, - /*high threshold=*/100.f, /*acceleration=*/10.f); - mFakePolicy->setVelocityControlParams(testParams); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyDeviceResetArgs resetArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); - ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); - - NotifyMotionArgs args; - - // Move and verify scale is applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_GT(relX, 10); - ASSERT_GT(relY, 20); - - // Enable Pointer Capture - mFakePolicy->setPointerCapture(true); - configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); - NotifyPointerCaptureChangedArgs captureArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); - ASSERT_TRUE(captureArgs.request.enable); - - // Move and verify scale is not applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_EQ(10, args.pointerCoords[0].getX()); - ASSERT_EQ(20, args.pointerCoords[0].getY()); -} - -TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyDeviceResetArgs resetArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); - ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); - - // Ensure the display is rotated. - prepareDisplay(ui::ROTATION_90); - - NotifyMotionArgs args; - - // Verify that the coordinates are rotated. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X)); - ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); - - // Enable Pointer Capture. - mFakePolicy->setPointerCapture(true); - configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); - NotifyPointerCaptureChangedArgs captureArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); - ASSERT_TRUE(captureArgs.request.enable); - - // Move and verify rotation is not applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_EQ(10, args.pointerCoords[0].getX()); - ASSERT_EQ(20, args.pointerCoords[0].getY()); -} - -TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) { - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_90); - - // Set up the secondary display as the display on which the pointer should be shown. - // The InputDevice is not associated with any display. - prepareSecondaryDisplay(); - mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); - mFakePointerController->setPosition(100, 200); - - // Ensure input events are generated for the secondary display. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), - WithCoords(110.0f, 220.0f)))); - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); -} - -TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) { - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_90); - - // Set up the secondary display as the display on which the pointer should be shown, - // and associate the InputDevice with the secondary display. - prepareSecondaryDisplay(); - mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); - mFakePointerController->setPosition(100, 200); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), - WithCoords(110.0f, 220.0f)))); - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); -} - -TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) { - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display as the display on which the pointer should be shown. - prepareDisplay(ui::ROTATION_90); - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - - // Associate the InputDevice with the secondary display. - prepareSecondaryDisplay(); - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - // The mapper should not generate any events because it is associated with a display that is - // different from the pointer display. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); -} - -// --- CursorInputMapperTestWithChoreographer --- - -// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging -// logic can be removed. -class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase { -protected: - void SetUp() override { - input_flags::enable_pointer_choreographer(true); - CursorInputMapperTestBase::SetUp(); - } -}; - -TEST_F(CursorInputMapperTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - InputDeviceInfo info; - mapper.populateDeviceInfo(info); - - // Initially there may not be a valid motion range. - ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); - ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, - AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); - - // When the viewport and the default pointer display ID is set, then there should be a valid - // motion range. - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - InputDeviceInfo info2; - mapper.populateDeviceInfo(info2); - - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0, - DISPLAY_WIDTH - 1, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0, - DISPLAY_HEIGHT - 1, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE, - AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - prepareDisplay(ui::ROTATION_0); - - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - - NotifyMotionArgs motionArgs; - NotifyKeyArgs keyArgs; - - // press BTN_LEFT, release BTN_LEFT - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, - motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, - motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - // press BTN_BACK, release BTN_BACK - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - // press BTN_SIDE, release BTN_SIDE - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - // press BTN_FORWARD, release BTN_FORWARD - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - // press BTN_EXTRA, release BTN_EXTRA - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); -} - -TEST_F(CursorInputMapperTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - prepareDisplay(ui::ROTATION_0); - - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - - NotifyMotionArgs args; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) { - addConfigurationProperty("cursor.mode", "pointer"); - const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, - /*highThreshold=*/100.f, /*acceleration=*/10.f); - mFakePolicy->setVelocityControlParams(testParams); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - prepareDisplay(ui::ROTATION_0); - - NotifyDeviceResetArgs resetArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); - ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); - - NotifyMotionArgs args; - - // Move and verify scale is applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_GT(relX, 10); - ASSERT_GT(relY, 20); - - // Enable Pointer Capture - mFakePolicy->setPointerCapture(true); - configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); - NotifyPointerCaptureChangedArgs captureArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); - ASSERT_TRUE(captureArgs.request.enable); - - // Move and verify scale is not applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - const float relX2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_EQ(10, relX2); - ASSERT_EQ(20, relY2); -} - -TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) { - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_90); - - // Set up the secondary display as the display on which the pointer should be shown. - // The InputDevice is not associated with any display. - prepareSecondaryDisplay(); - mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); - mFakePointerController->setPosition(100, 200); - - // Ensure input events are generated without display ID and coords, - // because they will be decided later by PointerChoreographer. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE), - WithCoords(0.0f, 0.0f)))); -} - -TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) { - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_90); - - // Set up the secondary display as the display on which the pointer should be shown, - // and associate the InputDevice with the secondary display. - prepareSecondaryDisplay(); - mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); - mFakePointerController->setPosition(100, 200); - - // Ensure input events are generated with associated display ID but not with coords, - // because the coords will be decided later by PointerChoreographer. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), - WithCoords(0.0f, 0.0f)))); -} - -TEST_F(CursorInputMapperTestWithChoreographer, - ConfigureDisplayIdShouldGenerateEventWithMismatchedPointerDisplay) { - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display as the display on which the pointer should be shown. - prepareDisplay(ui::ROTATION_90); - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - - // Associate the InputDevice with the secondary display. - prepareSecondaryDisplay(); - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - // With PointerChoreographer enabled, there could be a PointerController for the associated - // display even if it is different from the pointer display. So the mapper should generate an - // event. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), - WithCoords(0.0f, 0.0f)))); -} - -// --- BluetoothCursorInputMapperTest --- - -class BluetoothCursorInputMapperTest : public CursorInputMapperTestBase { -protected: - void SetUp() override { - input_flags::enable_pointer_choreographer(false); - InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); - - mFakePointerController = std::make_shared<FakePointerController>(); - mFakePolicy->setPointerController(mFakePointerController); - } -}; - -TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - nsecs_t kernelEventTime = ARBITRARY_TIME; - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // Process several events that come in quick succession, according to their timestamps. - for (int i = 0; i < 3; i++) { - constexpr static nsecs_t delta = ms2ns(1); - static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); - kernelEventTime += delta; - expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; - - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - } -} - -TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // Process several events with the same timestamp from the kernel. - // Ensure that we do not generate events too far into the future. - constexpr static int32_t numEvents = - MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; - for (int i = 0; i < numEvents; i++) { - expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - } - - // By processing more events with the same timestamp, we should not generate events with a - // timestamp that is more than the specified max time delta from the timestamp at its injection. - const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; - for (int i = 0; i < 3; i++) { - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(cappedEventTime)))); - } -} - -TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - nsecs_t kernelEventTime = ARBITRARY_TIME; - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp - // smoothening is not needed, its timestamp is not affected. - kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); - expectedEventTime = kernelEventTime; - - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); -} - -// --- BluetoothCursorInputMapperTestWithChoreographer --- - -class BluetoothCursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase { -protected: - void SetUp() override { - input_flags::enable_pointer_choreographer(true); - InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); - - mFakePointerController = std::make_shared<FakePointerController>(); - mFakePolicy->setPointerController(mFakePointerController); - } -}; - -TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmoothening) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_0); - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - nsecs_t kernelEventTime = ARBITRARY_TIME; - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // Process several events that come in quick succession, according to their timestamps. - for (int i = 0; i < 3; i++) { - constexpr static nsecs_t delta = ms2ns(1); - static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); - kernelEventTime += delta; - expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; - - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - } -} - -TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningIsCapped) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_0); - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // Process several events with the same timestamp from the kernel. - // Ensure that we do not generate events too far into the future. - constexpr static int32_t numEvents = - MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; - for (int i = 0; i < numEvents; i++) { - expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - } - - // By processing more events with the same timestamp, we should not generate events with a - // timestamp that is more than the specified max time delta from the timestamp at its injection. - const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; - for (int i = 0; i < 3; i++) { - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(cappedEventTime)))); - } -} - -TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningNotUsed) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_0); - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - nsecs_t kernelEventTime = ARBITRARY_TIME; - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp - // smoothening is not needed, its timestamp is not affected. - kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); - expectedEventTime = kernelEventTime; - - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); -} - // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { @@ -10971,15 +9567,16 @@ TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) { ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount()); } -TEST_F(MultiTouchInputMapperTest, ResetClearsTouchState) { +TEST_F(MultiTouchInputMapperTest, Reset_RepopulatesMultiTouchState) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); // First finger down. + constexpr int32_t x1 = 100, y1 = 200, x2 = 300, y2 = 400; processId(mapper, FIRST_TRACKING_ID); - processPosition(mapper, 100, 200); + processPosition(mapper, x1, y1); processPressure(mapper, RAW_PRESSURE_MAX); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( @@ -10988,42 +9585,49 @@ TEST_F(MultiTouchInputMapperTest, ResetClearsTouchState) { // Second finger down. processSlot(mapper, SECOND_SLOT); processId(mapper, SECOND_TRACKING_ID); - processPosition(mapper, 300, 400); + processPosition(mapper, x2, y2); processPressure(mapper, RAW_PRESSURE_MAX); processSync(mapper); ASSERT_NO_FATAL_FAILURE( mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN))); - // Reset the mapper. When the mapper is reset, the touch state is also cleared. + // Set MT Slot state to be repopulated for the required slots + std::vector<int32_t> mtSlotValues(RAW_SLOT_MAX + 1, -1); + mtSlotValues[0] = FIRST_TRACKING_ID; + mtSlotValues[1] = SECOND_TRACKING_ID; + mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_TRACKING_ID, mtSlotValues); + + mtSlotValues[0] = x1; + mtSlotValues[1] = x2; + mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_POSITION_X, mtSlotValues); + + mtSlotValues[0] = y1; + mtSlotValues[1] = y2; + mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_POSITION_Y, mtSlotValues); + + mtSlotValues[0] = RAW_PRESSURE_MAX; + mtSlotValues[1] = RAW_PRESSURE_MAX; + mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_PRESSURE, mtSlotValues); + + // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be + // repopulated. Resetting should cancel the ongoing gesture. resetMapper(mapper, ARBITRARY_TIME); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))); - // Move the second slot pointer, and ensure there are no events, because the touch state was - // cleared and no slots should be in use. + // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use + // the existing touch state to generate a down event. processPosition(mapper, 301, 302); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); - - // Release both fingers. - processId(mapper, INVALID_TRACKING_ID); - processSlot(mapper, FIRST_SLOT); - processId(mapper, INVALID_TRACKING_ID); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); - - // Start a new gesture, and ensure we get a DOWN event for it. - processId(mapper, FIRST_TRACKING_ID); - processPosition(mapper, 200, 300); - processPressure(mapper, RAW_PRESSURE_MAX); - processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(ACTION_POINTER_1_DOWN), WithPressure(1.f)))); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } -TEST_F(MultiTouchInputMapperTest, ResetClearsTouchStateWithNoPointersDown) { +TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(ui::ROTATION_0); prepareAxes(POSITION | ID | SLOT | PRESSURE); @@ -11151,66 +9755,6 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenConfigDisabled_ShouldNotShowDirect ASSERT_FALSE(fakePointerController->isPointerShown()); } -TEST_F(MultiTouchInputMapperTest, SimulateKernelBufferOverflow) { - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(ui::ROTATION_0); - prepareAxes(POSITION | ID | SLOT | PRESSURE); - MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); - - // First finger down. - processId(mapper, FIRST_TRACKING_ID); - processPosition(mapper, 100, 200); - processPressure(mapper, RAW_PRESSURE_MAX); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); - - // Assume the kernel buffer overflows, and we get a SYN_DROPPED event. - // This will reset the mapper, and thus also reset the touch state. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_DROPPED, 0); - resetMapper(mapper, ARBITRARY_TIME); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))); - - // Since the touch state was reset, it doesn't know which slots are active, so any movements - // are ignored. - processPosition(mapper, 101, 201); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); - - // Second finger goes down. This is the first active finger, so we get a DOWN event. - processSlot(mapper, SECOND_SLOT); - processId(mapper, SECOND_TRACKING_ID); - processPosition(mapper, 400, 500); - processPressure(mapper, RAW_PRESSURE_MAX); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); - - // First slot is still ignored, only the second one is active. - processSlot(mapper, FIRST_SLOT); - processPosition(mapper, 102, 202); - processSlot(mapper, SECOND_SLOT); - processPosition(mapper, 401, 501); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); - - // Both slots up, so we get the UP event for the active pointer. - processSlot(mapper, FIRST_SLOT); - processId(mapper, INVALID_TRACKING_ID); - processSlot(mapper, SECOND_SLOT); - processId(mapper, INVALID_TRACKING_ID); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE( - mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP))); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); -} - // --- MultiTouchInputMapperTest_ExternalDevice --- class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest { diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 73949136f4..db8916880a 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -48,7 +48,7 @@ namespace android { class MockInputReaderContext : public InputReaderContext { public: MOCK_METHOD(void, updateGlobalMetaState, (), (override)); - int32_t getGlobalMetaState() override { return 0; }; + MOCK_METHOD(int32_t, getGlobalMetaState, (), (override)); MOCK_METHOD(void, disableVirtualKeysUntil, (nsecs_t time), (override)); MOCK_METHOD(bool, shouldDropVirtualKey, (nsecs_t now, int32_t keyCode, int32_t scanCode), @@ -132,6 +132,9 @@ public: MOCK_METHOD(status_t, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis, int32_t* outValue), (const, override)); + MOCK_METHOD(base::Result<std::vector<int32_t>>, getMtSlotValues, + (int32_t deviceId, int32_t axis, size_t slotCount), (const, override)); + MOCK_METHOD(int32_t, getKeyCodeForKeyLocation, (int32_t deviceId, int32_t locationKeyCode), (const, override)); MOCK_METHOD(bool, markSupportedKeyCodes, diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index 2ef79999a2..b44529bd04 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -55,6 +55,7 @@ protected: void SetUp() override { InputMapperUnitTest::SetUp(); + createDevice(); // set key-codes expected in tests for (const auto& [scanCode, outKeycode] : mKeyCodeMap) { diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp new file mode 100644 index 0000000000..d726385240 --- /dev/null +++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp @@ -0,0 +1,276 @@ +/* + * 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 "MultiTouchInputMapper.h" + +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <list> +#include <optional> + +#include "InputMapperTest.h" +#include "InterfaceMocks.h" +#include "TestEventMatchers.h" + +#define TAG "MultiTouchpadInputMapperUnit_test" + +namespace android { + +using testing::_; +using testing::IsEmpty; +using testing::Return; +using testing::SetArgPointee; +using testing::VariantWith; + +static constexpr int32_t DISPLAY_ID = 0; +static constexpr int32_t DISPLAY_WIDTH = 480; +static constexpr int32_t DISPLAY_HEIGHT = 800; +static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified +static constexpr int32_t SLOT_COUNT = 5; + +static constexpr int32_t ACTION_POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +static constexpr int32_t ACTION_POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + +/** + * Unit tests for MultiTouchInputMapper. + */ +class MultiTouchInputMapperUnitTest : public InputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + + // Present scan codes + expectScanCodes(/*present=*/true, + {BTN_TOUCH, BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP, + BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP}); + + // Missing scan codes that the mapper checks for. + expectScanCodes(/*present=*/false, + {BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_BRUSH, BTN_TOOL_PENCIL, + BTN_TOOL_AIRBRUSH}); + + // Current scan code state - all keys are UP by default + setScanCodeState(KeyState::UP, {BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, + BTN_BACK, BTN_SIDE, BTN_FORWARD, + BTN_EXTRA, BTN_TASK, BTN_TOUCH, + BTN_STYLUS, BTN_STYLUS2, BTN_0, + BTN_TOOL_FINGER, BTN_TOOL_PEN, BTN_TOOL_RUBBER, + BTN_TOOL_BRUSH, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH, + BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOOL_DOUBLETAP, + BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP}); + + setKeyCodeState(KeyState::UP, + {AKEYCODE_STYLUS_BUTTON_PRIMARY, AKEYCODE_STYLUS_BUTTON_SECONDARY}); + + // Input properties - only INPUT_PROP_DIRECT for touchscreen + EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT)) + .WillRepeatedly(Return(true)); + + // Axes that the device has + setupAxis(ABS_MT_SLOT, /*valid=*/true, /*min=*/0, /*max=*/SLOT_COUNT - 1, /*resolution=*/0); + setupAxis(ABS_MT_TRACKING_ID, /*valid=*/true, /*min*/ 0, /*max=*/255, /*resolution=*/0); + setupAxis(ABS_MT_POSITION_X, /*valid=*/true, /*min=*/0, /*max=*/2000, /*resolution=*/24); + setupAxis(ABS_MT_POSITION_Y, /*valid=*/true, /*min=*/0, /*max=*/1000, /*resolution=*/24); + + // Axes that the device does not have + setupAxis(ABS_MT_PRESSURE, /*valid=*/false, /*min*/ 0, /*max=*/255, /*resolution=*/0); + setupAxis(ABS_MT_ORIENTATION, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_TOUCH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + + // reset current slot at the beginning + EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _)) + .WillRepeatedly([](int32_t, int32_t, int32_t* outValue) { + *outValue = 0; + return OK; + }); + + // mark all slots not in use + mockSlotValues({}); + + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, + ViewportType::INTERNAL); + createDevice(); + mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + } + + // Mocks position and tracking Ids for the provided slots. Remaining slots will be marked + // unused. + void mockSlotValues( + const std::unordered_map<int32_t /*slotIndex*/, + std::pair<Point /*position*/, int32_t /*trackingId*/>>& + slotValues) { + EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, _, SLOT_COUNT)) + .WillRepeatedly([=](int32_t, int32_t axis, + size_t slotCount) -> base::Result<std::vector<int32_t>> { + // tracking Id for the unused slots must set to be < 0 + std::vector<int32_t> outMtSlotValues(slotCount + 1, -1); + outMtSlotValues[0] = axis; + switch (axis) { + case ABS_MT_POSITION_X: + for (const auto& [slotIndex, valuePair] : slotValues) { + outMtSlotValues[slotIndex] = valuePair.first.x; + } + return outMtSlotValues; + case ABS_MT_POSITION_Y: + for (const auto& [slotIndex, valuePair] : slotValues) { + outMtSlotValues[slotIndex] = valuePair.first.y; + } + return outMtSlotValues; + case ABS_MT_TRACKING_ID: + for (const auto& [slotIndex, valuePair] : slotValues) { + outMtSlotValues[slotIndex] = valuePair.second; + } + return outMtSlotValues; + default: + return base::ResultError("Axis not supported", NAME_NOT_FOUND); + } + }); + } + + std::list<NotifyArgs> processPosition(int32_t x, int32_t y) { + std::list<NotifyArgs> args; + args += process(EV_ABS, ABS_MT_POSITION_X, x); + args += process(EV_ABS, ABS_MT_POSITION_Y, y); + return args; + } + + std::list<NotifyArgs> processId(int32_t id) { return process(EV_ABS, ABS_MT_TRACKING_ID, id); } + + std::list<NotifyArgs> processKey(int32_t code, int32_t value) { + return process(EV_KEY, code, value); + } + + std::list<NotifyArgs> processSlot(int32_t slot) { return process(EV_ABS, ABS_MT_SLOT, slot); } + + std::list<NotifyArgs> processSync() { return process(EV_SYN, SYN_REPORT, 0); } +}; + +// This test simulates a multi-finger gesture with unexpected reset in between. This might happen +// due to buffer overflow and device with report a SYN_DROPPED. In this case we expect mapper to be +// reset, MT slot state to be re-populated and the gesture should be cancelled and restarted. +TEST_F(MultiTouchInputMapperUnitTest, MultiFingerGestureWithUnexpectedReset) { + std::list<NotifyArgs> args; + + // Two fingers down at once. + constexpr int32_t FIRST_TRACKING_ID = 1, SECOND_TRACKING_ID = 2; + int32_t x1 = 100, y1 = 125, x2 = 200, y2 = 225; + processKey(BTN_TOUCH, 1); + args += processPosition(x1, y1); + args += processId(FIRST_TRACKING_ID); + args += processSlot(1); + args += processPosition(x2, y2); + args += processId(SECOND_TRACKING_ID); + ASSERT_THAT(args, IsEmpty()); + + args = processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(ACTION_POINTER_1_DOWN)))); + + // Move. + x1 += 10; + y1 += 15; + x2 += 5; + y2 -= 10; + args = processSlot(0); + args += processPosition(x1, y1); + args += processSlot(1); + args += processPosition(x2, y2); + ASSERT_THAT(args, IsEmpty()); + + args = processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))); + const auto pointerCoordsBeforeReset = std::get<NotifyMotionArgs>(args.back()).pointerCoords; + + // On buffer overflow mapper will be reset and MT slots data will be repopulated + EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _)) + .WillRepeatedly([=](int32_t, int32_t, int32_t* outValue) { + *outValue = 1; + return OK; + }); + + mockSlotValues( + {{1, {Point{x1, y1}, FIRST_TRACKING_ID}}, {2, {Point{x2, y2}, SECOND_TRACKING_ID}}}); + + setScanCodeState(KeyState::DOWN, {BTN_TOUCH}); + + args = mMapper->reset(systemTime(SYSTEM_TIME_MONOTONIC)); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)))); + + // SYN_REPORT should restart the gesture again + args = processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(ACTION_POINTER_1_DOWN)))); + ASSERT_EQ(std::get<NotifyMotionArgs>(args.back()).pointerCoords, pointerCoordsBeforeReset); + + // Move. + x1 += 10; + y1 += 15; + x2 += 5; + y2 -= 10; + args = processSlot(0); + args += processPosition(x1, y1); + args += processSlot(1); + args += processPosition(x2, y2); + ASSERT_THAT(args, IsEmpty()); + + args = processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))); + + // First finger up. + args = processSlot(0); + args += processId(-1); + ASSERT_THAT(args, IsEmpty()); + + args = processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_POINTER_0_UP)))); + + // Second finger up. + processKey(BTN_TOUCH, 0); + args = processSlot(1); + args += processId(-1); + ASSERT_THAT(args, IsEmpty()); + + args = processSync(); + ASSERT_THAT(args, + ElementsAre( + VariantWith<NotifyMotionArgs>(WithMotionAction(AMOTION_EVENT_ACTION_UP)))); +} + +} // namespace android diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp index 9fa6cdd298..5e67506b48 100644 --- a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp +++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp @@ -23,6 +23,11 @@ class MultiTouchMotionAccumulatorTest : public InputMapperUnitTest { protected: static constexpr size_t SLOT_COUNT = 8; + void SetUp() override { + InputMapperUnitTest::SetUp(); + createDevice(); + } + MultiTouchMotionAccumulator mMotionAccumulator; void processMotionEvent(int32_t type, int32_t code, int32_t value) { diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 193b84d313..e9e50619ee 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -145,15 +145,18 @@ private: }; TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { - const std::vector<NotifyArgs> allArgs{NotifyInputDevicesChangedArgs{}, - NotifyConfigurationChangedArgs{}, - NotifyKeyArgs{}, - NotifyMotionArgs{}, - NotifySensorArgs{}, - NotifySwitchArgs{}, - NotifyDeviceResetArgs{}, - NotifyPointerCaptureChangedArgs{}, - NotifyVibratorStateArgs{}}; + const std::vector<NotifyArgs> + allArgs{NotifyInputDevicesChangedArgs{}, + NotifyConfigurationChangedArgs{}, + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build(), + MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(FIRST_TOUCH_POINTER) + .build(), + NotifySensorArgs{}, + NotifySwitchArgs{}, + NotifyDeviceResetArgs{}, + NotifyPointerCaptureChangedArgs{}, + NotifyVibratorStateArgs{}}; for (auto notifyArgs : allArgs) { mChoreographer.notify(notifyArgs); @@ -1444,6 +1447,15 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForStylus) { ASSERT_FALSE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, SECOND_DEVICE_ID)); pc->assertPointerIconNotSet(); + + // The stylus stops hovering. This should cause the icon to be reset. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertPointerIconSet(PointerIconStyle::TYPE_NOT_SPECIFIED); } TEST_F(PointerChoreographerTest, SetsCustomPointerIconForStylus) { @@ -1545,4 +1557,122 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouseAndStylus) { mousePc->assertPointerIconNotSet(); } +TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerOnDisplay) { + // Make sure there are two PointerControllers on different displays. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE), + generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}}); + auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId()); + auto secondMousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(ANOTHER_DISPLAY_ID, secondMousePc->getDisplayId()); + + // Both pointers should be visible. + ASSERT_TRUE(firstMousePc->isPointerShown()); + ASSERT_TRUE(secondMousePc->isPointerShown()); + + // Hide the icon on the second display. + mChoreographer.setPointerIconVisibility(ANOTHER_DISPLAY_ID, false); + ASSERT_TRUE(firstMousePc->isPointerShown()); + ASSERT_FALSE(secondMousePc->isPointerShown()); + + // Move and set pointer icons for both mice. The second pointer should still be hidden. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(ANOTHER_DISPLAY_ID) + .build()); + ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID)); + ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, ANOTHER_DISPLAY_ID, + SECOND_DEVICE_ID)); + firstMousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT); + secondMousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT); + ASSERT_TRUE(firstMousePc->isPointerShown()); + ASSERT_FALSE(secondMousePc->isPointerShown()); + + // Allow the icon to be visible on the second display, and move the mouse. + mChoreographer.setPointerIconVisibility(ANOTHER_DISPLAY_ID, true); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(ANOTHER_DISPLAY_ID) + .build()); + ASSERT_TRUE(firstMousePc->isPointerShown()); + ASSERT_TRUE(secondMousePc->isPointerShown()); +} + +TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerWhenDeviceConnected) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // Hide the pointer on the display, and then connect the mouse. + mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, mousePc->getDisplayId()); + + // The pointer should not be visible. + ASSERT_FALSE(mousePc->isPointerShown()); +} + +TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForTouchpad) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // Hide the pointer on the display. + mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + auto touchpadPc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, touchpadPc->getDisplayId()); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, + AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + + // The pointer should not be visible. + ASSERT_FALSE(touchpadPc->isPointerShown()); +} + +TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForStylus) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setStylusPointerIconEnabled(true); + + // Hide the pointer on the display. + mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID)); + auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + pc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT); + + // The pointer should not be visible. + ASSERT_FALSE(pc->isPointerShown()); +} + } // namespace android diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index 66fdaa4520..a3e8eafea3 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -18,6 +18,7 @@ #include <cmath> #include <compare> +#include <ios> #include <android-base/stringprintf.h> #include <android/input.h> @@ -464,6 +465,53 @@ inline WithPointersMatcher WithPointers( return WithPointersMatcher(pointers); } +/// Pointer ids matcher +class WithPointerIdsMatcher { +public: + using is_gtest_matcher = void; + explicit WithPointerIdsMatcher(std::set<int32_t> pointerIds) : mPointerIds(pointerIds) {} + + bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const { + std::set<int32_t> actualPointerIds; + for (size_t pointerIndex = 0; pointerIndex < event.getPointerCount(); pointerIndex++) { + const PointerProperties* properties = event.getPointerProperties(pointerIndex); + actualPointerIds.insert(properties->id); + } + + if (mPointerIds != actualPointerIds) { + *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got " + << dumpSet(actualPointerIds); + return false; + } + return true; + } + + bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const { + std::set<int32_t> actualPointerIds; + for (const PointerProperties& properties : event.pointerProperties) { + actualPointerIds.insert(properties.id); + } + + if (mPointerIds != actualPointerIds) { + *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got " + << dumpSet(actualPointerIds); + return false; + } + return true; + } + + void DescribeTo(std::ostream* os) const { *os << "with pointer ids " << dumpSet(mPointerIds); } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointer ids"; } + +private: + const std::set<int32_t> mPointerIds; +}; + +inline WithPointerIdsMatcher WithPointerIds(const std::set<int32_t /*id*/>& pointerIds) { + return WithPointerIdsMatcher(pointerIds); +} + /// Key code class WithKeyCodeMatcher { public: @@ -631,6 +679,24 @@ MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") { return argPressure == pressure; } +MATCHER_P(WithSize, size, "MotionEvent with specified size") { + const auto argSize = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE); + *result_listener << "expected size " << size << ", but got " << argSize; + return argSize == size; +} + +MATCHER_P(WithOrientation, orientation, "MotionEvent with specified orientation") { + const auto argOrientation = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + *result_listener << "expected orientation " << orientation << ", but got " << argOrientation; + return argOrientation == orientation; +} + +MATCHER_P(WithDistance, distance, "MotionEvent with specified distance") { + const auto argDistance = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_DISTANCE); + *result_listener << "expected distance " << distance << ", but got " << argDistance; + return argDistance == distance; +} + MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") { const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); @@ -674,6 +740,12 @@ MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") { return arg.buttonState == buttons; } +MATCHER_P(WithMetaState, metaState, "InputEvent with specified meta state") { + *result_listener << "expected meta state 0x" << std::hex << metaState << ", but got 0x" + << arg.metaState; + return arg.metaState == metaState; +} + MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") { *result_listener << "expected action button " << actionButton << ", but got " << arg.actionButton; @@ -696,4 +768,16 @@ MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified pr return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision; } +MATCHER_P(WithPolicyFlags, policyFlags, "InputEvent with specified policy flags") { + *result_listener << "expected policy flags 0x" << std::hex << policyFlags << ", but got 0x" + << arg.policyFlags; + return arg.policyFlags == static_cast<uint32_t>(policyFlags); +} + +MATCHER_P(WithEdgeFlags, edgeFlags, "InputEvent with specified edge flags") { + *result_listener << "expected edge flags 0x" << std::hex << edgeFlags << ", but got 0x" + << arg.edgeFlags; + return arg.edgeFlags == edgeFlags; +} + } // namespace android diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp index 8cf738cf4f..a92dce5dd0 100644 --- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp +++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp @@ -37,6 +37,8 @@ constexpr auto ACTION_UP = AMOTION_EVENT_ACTION_UP; constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS; constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; +constexpr auto HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER; +constexpr auto HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT; constexpr int32_t DISPLAY_ID = 0; constexpr int32_t DISPLAY_WIDTH = 480; constexpr int32_t DISPLAY_HEIGHT = 800; @@ -101,14 +103,25 @@ protected: setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_TRACKING_ID, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, testing::_)) .WillRepeatedly([](int32_t eventHubId, int32_t, int32_t* outValue) { *outValue = 0; return OK; }); - mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration); + EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, testing::_, testing::_)) + .WillRepeatedly([]() -> base::Result<std::vector<int32_t>> { + return base::ResultError("Axis not supported", NAME_NOT_FOUND); + }); + createDevice(); + mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration, + isPointerChoreographerEnabled()); } + + virtual bool isPointerChoreographerEnabled() { return false; } }; class TouchpadInputMapperTest : public TouchpadInputMapperTestBase { @@ -150,12 +163,14 @@ TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { setScanCodeState(KeyState::UP, {BTN_LEFT}); args += process(EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), - VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)))); // Liftoff args.clear(); @@ -170,10 +185,9 @@ TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { class TouchpadInputMapperTestWithChoreographer : public TouchpadInputMapperTestBase { protected: - void SetUp() override { - input_flags::enable_pointer_choreographer(true); - TouchpadInputMapperTestBase::SetUp(); - } + void SetUp() override { TouchpadInputMapperTestBase::SetUp(); } + + bool isPointerChoreographerEnabled() override { return true; } }; // TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging @@ -217,12 +231,14 @@ TEST_F(TouchpadInputMapperTestWithChoreographer, HoverAndLeftButtonPress) { setScanCodeState(KeyState::UP, {BTN_LEFT}); args += process(EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), - VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)))); // Liftoff args.clear(); diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 81c570d77b..7898126944 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -201,6 +201,18 @@ public: int32_t* outValue) const override { return mFdp->ConsumeIntegral<status_t>(); } + base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis, + size_t slotCount) const override { + if (mFdp->ConsumeBool()) { + std::vector<int32_t> outValues(slotCount + 1); + for (size_t i = 0; i < outValues.size(); i++) { + outValues.push_back(mFdp->ConsumeIntegral<int32_t>()); + } + return std::move(outValues); + } else { + return base::ResultError("Fuzzer", UNKNOWN_ERROR); + } + } bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) const override { return mFdp->ConsumeBool(); diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp index be765cc364..c2bf275611 100644 --- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp @@ -123,6 +123,7 @@ void setTouchpadSettings(ThreadSafeFuzzedDataProvider& fdp, InputReaderConfigura config.touchpadPointerSpeed = fdp.ConsumeIntegralInRange(-7, 7); config.touchpadNaturalScrollingEnabled = fdp.ConsumeBool(); config.touchpadTapToClickEnabled = fdp.ConsumeBool(); + config.touchpadTapDraggingEnabled = fdp.ConsumeBool(); config.touchpadRightClickZoneEnabled = fdp.ConsumeBool(); } diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp index 9a23c848c9..bc178bce8f 100644 --- a/services/powermanager/PowerHalController.cpp +++ b/services/powermanager/PowerHalController.cpp @@ -80,7 +80,7 @@ std::shared_ptr<HalWrapper> PowerHalController::initHal() { // Check if a call to Power HAL function failed; if so, log the failure and // invalidate the current Power HAL handle. template <typename T> -HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const char* fnName) { +HalResult<T> PowerHalController::processHalResult(HalResult<T>&& result, const char* fnName) { if (result.isFailed()) { ALOGE("%s failed: %s", fnName, result.errorMessage()); std::lock_guard<std::mutex> lock(mConnectedHalMutex); @@ -88,21 +88,19 @@ HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const cha mConnectedHal = nullptr; mHalConnector->reset(); } - return result; + return std::move(result); } HalResult<void> PowerHalController::setBoost(aidl::android::hardware::power::Boost boost, int32_t durationMs) { std::shared_ptr<HalWrapper> handle = initHal(); - auto result = handle->setBoost(boost, durationMs); - return processHalResult(result, "setBoost"); + return processHalResult(handle->setBoost(boost, durationMs), "setBoost"); } HalResult<void> PowerHalController::setMode(aidl::android::hardware::power::Mode mode, bool enabled) { std::shared_ptr<HalWrapper> handle = initHal(); - auto result = handle->setMode(mode, enabled); - return processHalResult(result, "setMode"); + return processHalResult(handle->setMode(mode, enabled), "setMode"); } HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> @@ -110,14 +108,35 @@ PowerHalController::createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) { std::shared_ptr<HalWrapper> handle = initHal(); - auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos); - return processHalResult(result, "createHintSession"); + return processHalResult(handle->createHintSession(tgid, uid, threadIds, durationNanos), + "createHintSession"); +} + +HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> +PowerHalController::createHintSessionWithConfig( + int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) { + std::shared_ptr<HalWrapper> handle = initHal(); + return processHalResult(handle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, + tag, config), + "createHintSessionWithConfig"); } HalResult<int64_t> PowerHalController::getHintSessionPreferredRate() { std::shared_ptr<HalWrapper> handle = initHal(); - auto result = handle->getHintSessionPreferredRate(); - return processHalResult(result, "getHintSessionPreferredRate"); + return processHalResult(handle->getHintSessionPreferredRate(), "getHintSessionPreferredRate"); +} + +HalResult<aidl::android::hardware::power::ChannelConfig> PowerHalController::getSessionChannel( + int tgid, int uid) { + std::shared_ptr<HalWrapper> handle = initHal(); + return processHalResult(handle->getSessionChannel(tgid, uid), "getSessionChannel"); +} + +HalResult<void> PowerHalController::closeSessionChannel(int tgid, int uid) { + std::shared_ptr<HalWrapper> handle = initHal(); + return processHalResult(handle->closeSessionChannel(tgid, uid), "closeSessionChannel"); } } // namespace power diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp index 76afbfc646..1009100cc2 100644 --- a/services/powermanager/PowerHalWrapper.cpp +++ b/services/powermanager/PowerHalWrapper.cpp @@ -42,37 +42,58 @@ inline HalResult<void> toHalResult(const ndk::ScopedAStatus& result) { // ------------------------------------------------------------------------------------------------- HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) { - ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available", - toString(boost).c_str(), durationMs); + ALOGV("Skipped setBoost %s with duration %dms because %s", toString(boost).c_str(), durationMs, + getUnsupportedMessage()); return HalResult<void>::unsupported(); } HalResult<void> EmptyHalWrapper::setMode(Aidl::Mode mode, bool enabled) { - ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(), - enabled ? "true" : "false"); + ALOGV("Skipped setMode %s to %s because %s", toString(mode).c_str(), enabled ? "true" : "false", + getUnsupportedMessage()); return HalResult<void>::unsupported(); } HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession( int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) { - ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available", - threadIds.size()); + ALOGV("Skipped createHintSession(task num=%zu) because %s", threadIds.size(), + getUnsupportedMessage()); + return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported(); +} + +HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSessionWithConfig( + int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t, Aidl::SessionTag, + Aidl::SessionConfig*) { + ALOGV("Skipped createHintSessionWithConfig(task num=%zu) because %s", threadIds.size(), + getUnsupportedMessage()); return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported(); } HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() { - ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available"); + ALOGV("Skipped getHintSessionPreferredRate because %s", getUnsupportedMessage()); return HalResult<int64_t>::unsupported(); } +HalResult<Aidl::ChannelConfig> EmptyHalWrapper::getSessionChannel(int, int) { + ALOGV("Skipped getSessionChannel because %s", getUnsupportedMessage()); + return HalResult<Aidl::ChannelConfig>::unsupported(); +} + +HalResult<void> EmptyHalWrapper::closeSessionChannel(int, int) { + ALOGV("Skipped closeSessionChannel because %s", getUnsupportedMessage()); + return HalResult<void>::unsupported(); +} + +const char* EmptyHalWrapper::getUnsupportedMessage() { + return "Power HAL is not supported"; +} + // ------------------------------------------------------------------------------------------------- HalResult<void> HidlHalWrapperV1_0::setBoost(Aidl::Boost boost, int32_t durationMs) { if (boost == Aidl::Boost::INTERACTION) { return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs); } else { - ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str()); - return HalResult<void>::unsupported(); + return EmptyHalWrapper::setBoost(boost, durationMs); } } @@ -92,9 +113,7 @@ HalResult<void> HidlHalWrapperV1_0::setMode(Aidl::Mode mode, bool enabled) { case Aidl::Mode::DOUBLE_TAP_TO_WAKE: return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled); default: - ALOGV("Skipped setMode %s because Power HAL AIDL not available", - toString(mode).c_str()); - return HalResult<void>::unsupported(); + return EmptyHalWrapper::setMode(mode, enabled); } } @@ -113,16 +132,8 @@ HalResult<void> HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabl return HalResult<void>::fromReturn(ret); } -HalResult<std::shared_ptr<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession( - int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) { - ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available", - threadIds.size()); - return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported(); -} - -HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() { - ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available"); - return HalResult<int64_t>::unsupported(); +const char* HidlHalWrapperV1_0::getUnsupportedMessage() { + return "Power HAL AIDL is not supported"; } // ------------------------------------------------------------------------------------------------- @@ -191,7 +202,7 @@ HalResult<void> AidlHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) // Quick return if boost is not supported by HAL if (idx >= mBoostSupportedArray.size() || mBoostSupportedArray[idx] == HalSupport::OFF) { - ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str()); + ALOGV("Skipped setBoost %s because %s", toString(boost).c_str(), getUnsupportedMessage()); return HalResult<void>::unsupported(); } @@ -207,8 +218,8 @@ HalResult<void> AidlHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; if (!isSupported) { - ALOGV("Skipped setBoost %s because Power HAL doesn't support it", - toString(boost).c_str()); + ALOGV("Skipped setBoost %s because %s", toString(boost).c_str(), + getUnsupportedMessage()); return HalResult<void>::unsupported(); } } @@ -223,7 +234,7 @@ HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) { // Quick return if mode is not supported by HAL if (idx >= mModeSupportedArray.size() || mModeSupportedArray[idx] == HalSupport::OFF) { - ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str()); + ALOGV("Skipped setMode %s because %s", toString(mode).c_str(), getUnsupportedMessage()); return HalResult<void>::unsupported(); } @@ -236,8 +247,7 @@ HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) { mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; if (!isSupported) { - ALOGV("Skipped setMode %s because Power HAL doesn't support it", - toString(mode).c_str()); + ALOGV("Skipped setMode %s because %s", toString(mode).c_str(), getUnsupportedMessage()); return HalResult<void>::unsupported(); } } @@ -251,7 +261,17 @@ HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSe std::shared_ptr<Aidl::IPowerHintSession> appSession; return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>:: fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession), - appSession); + std::move(appSession)); +} + +HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSessionWithConfig( + int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, + Aidl::SessionTag tag, Aidl::SessionConfig* config) { + std::shared_ptr<Aidl::IPowerHintSession> appSession; + return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>:: + fromStatus(mHandle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, + tag, config, &appSession), + std::move(appSession)); } HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() { @@ -260,6 +280,20 @@ HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() { return HalResult<int64_t>::fromStatus(result, rate); } +HalResult<Aidl::ChannelConfig> AidlHalWrapper::getSessionChannel(int tgid, int uid) { + Aidl::ChannelConfig config; + auto result = mHandle->getSessionChannel(tgid, uid, &config); + return HalResult<Aidl::ChannelConfig>::fromStatus(result, std::move(config)); +} + +HalResult<void> AidlHalWrapper::closeSessionChannel(int tgid, int uid) { + return toHalResult(mHandle->closeSessionChannel(tgid, uid)); +} + +const char* AidlHalWrapper::getUnsupportedMessage() { + return "Power HAL doesn't support it"; +} + // ------------------------------------------------------------------------------------------------- } // namespace power diff --git a/services/powermanager/WorkDuration.cpp b/services/powermanager/WorkDuration.cpp index ef723c229c..bd2b10a149 100644 --- a/services/powermanager/WorkDuration.cpp +++ b/services/powermanager/WorkDuration.cpp @@ -25,8 +25,9 @@ namespace android::os { WorkDuration::WorkDuration(int64_t startTimestampNanos, int64_t totalDurationNanos, int64_t cpuDurationNanos, int64_t gpuDurationNanos) - : workPeriodStartTimestampNanos(startTimestampNanos), + : timestampNanos(0), actualTotalDurationNanos(totalDurationNanos), + workPeriodStartTimestampNanos(startTimestampNanos), actualCpuDurationNanos(cpuDurationNanos), actualGpuDurationNanos(gpuDurationNanos) {} diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp index 759485ffce..61ab47a75a 100644 --- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp +++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp @@ -70,12 +70,14 @@ static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower: if (hal == nullptr) { ALOGV("Power HAL not available, skipping test..."); + state.SkipWithMessage("Power HAL unavailable"); return; } ndk::ScopedAStatus ret = (*hal.*fn)(std::forward<Args1>(args1)...); if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { ALOGV("Power HAL does not support this operation, skipping test..."); + state.SkipWithMessage("operation unsupported"); return; } @@ -97,6 +99,7 @@ static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::* if (hal == nullptr) { ALOGV("Power HAL not available, skipping test..."); + state.SkipWithMessage("Power HAL unavailable"); return; } @@ -109,12 +112,14 @@ static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::* if (session == nullptr) { ALOGV("Power HAL doesn't support session, skipping test..."); + state.SkipWithMessage("operation unsupported"); return; } ndk::ScopedAStatus ret = (*session.*fn)(std::forward<Args1>(args1)...); if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { ALOGV("Power HAL does not support this operation, skipping test..."); + state.SkipWithMessage("operation unsupported"); return; } @@ -163,6 +168,7 @@ static void BM_PowerHalAidlBenchmarks_createHintSession(benchmark::State& state) if (hal == nullptr) { ALOGV("Power HAL not available, skipping test..."); + state.SkipWithMessage("Power HAL unavailable"); return; } @@ -170,6 +176,7 @@ static void BM_PowerHalAidlBenchmarks_createHintSession(benchmark::State& state) hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession); if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { ALOGV("Power HAL does not support this operation, skipping test..."); + state.SkipWithMessage("operation unsupported"); return; } diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp index 111b5d70bc..bcb376b799 100644 --- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp +++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp @@ -50,6 +50,7 @@ static void runBenchmark(benchmark::State& state, microseconds delay, Return<R> if (hal == nullptr) { ALOGV("Power HAL HIDL not available, skipping test..."); + state.SkipWithMessage("Power HAL unavailable"); return; } diff --git a/services/powermanager/include/android/WorkDuration.h b/services/powermanager/include/android/WorkDuration.h index 99b5b8b1b4..26a575f834 100644 --- a/services/powermanager/include/android/WorkDuration.h +++ b/services/powermanager/include/android/WorkDuration.h @@ -61,11 +61,11 @@ struct WorkDuration : AWorkDuration, android::Parcelable { return os; } - int64_t workPeriodStartTimestampNanos; + int64_t timestampNanos; int64_t actualTotalDurationNanos; + int64_t workPeriodStartTimestampNanos; int64_t actualCpuDurationNanos; int64_t actualGpuDurationNanos; - int64_t timestampNanos; }; } // namespace android::os diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index 641ba9f44b..a7202969ad 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -29,9 +29,12 @@ #include <thread> using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::ChannelConfig; using aidl::android::hardware::power::IPower; using aidl::android::hardware::power::IPowerHintSession; using aidl::android::hardware::power::Mode; +using aidl::android::hardware::power::SessionConfig; +using aidl::android::hardware::power::SessionTag; using android::binder::Status; using namespace android; @@ -53,6 +56,14 @@ public: (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session), (override)); + MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig, + (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, SessionTag tag, SessionConfig* config, + std::shared_ptr<IPowerHintSession>* _aidl_return), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel, + (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override)); + MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override)); MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); @@ -245,6 +256,23 @@ TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionSuccessful) { ASSERT_TRUE(result.isOk()); } +TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionWithConfigSuccessful) { + std::vector<int> threadIds{gettid()}; + int32_t tgid = 999; + int32_t uid = 1001; + int64_t durationNanos = 16666666L; + SessionTag tag = SessionTag::OTHER; + SessionConfig out; + EXPECT_CALL(*mMockHal.get(), + createHintSessionWithConfig(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), + Eq(tag), _, _)) + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); + auto result = + mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out); + ASSERT_TRUE(result.isOk()); +} + TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionFailed) { int32_t tgid = 999; int32_t uid = 1001; @@ -268,3 +296,18 @@ TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) { int64_t rate = result.value(); ASSERT_GE(0, rate); } + +TEST_F(PowerHalWrapperAidlTest, TestSessionChannel) { + int32_t tgid = 999; + int32_t uid = 1001; + EXPECT_CALL(*mMockHal.get(), getSessionChannel(Eq(tgid), Eq(uid), _)) + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), closeSessionChannel(Eq(tgid), Eq(uid))) + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); + auto createResult = mWrapper->getSessionChannel(tgid, uid); + ASSERT_TRUE(createResult.isOk()); + auto closeResult = mWrapper->closeSessionChannel(tgid, uid); + ASSERT_TRUE(closeResult.isOk()); +} diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp index e60db93431..91c2d1f3be 100644 --- a/services/sensorservice/AidlSensorHalWrapper.cpp +++ b/services/sensorservice/AidlSensorHalWrapper.cpp @@ -178,6 +178,11 @@ ssize_t AidlSensorHalWrapper::pollFmq(sensors_event_t *buffer, size_t maxNumEven if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) { ALOGD("Event FMQ internal wake, returning from poll with no events"); return DEAD_OBJECT; + } else if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mInHalBypassMode && + availableEvents == 0) { + ALOGD("Event FMQ internal wake due to HAL Bypass Mode, returning from poll with no " + "events"); + return OK; } } @@ -221,6 +226,17 @@ std::vector<sensor_t> AidlSensorHalWrapper::getSensorsList() { status_t AidlSensorHalWrapper::setOperationMode(SensorService::Mode mode) { if (mSensors == nullptr) return NO_INIT; + if (mode == SensorService::Mode::HAL_BYPASS_REPLAY_DATA_INJECTION) { + if (!mInHalBypassMode) { + mInHalBypassMode = true; + mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE)); + } + return OK; + } else { + if (mInHalBypassMode) { + mInHalBypassMode = false; + } + } return convertToStatus(mSensors->setOperationMode(static_cast<ISensors::OperationMode>(mode))); } diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 11c56a8c56..afaf0ae84f 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -7,6 +7,19 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "sensorservice_flags", + package: "com.android.frameworks.sensorservice.flags", + container: "system", + srcs: ["senserservice_flags.aconfig"], +} + +cc_aconfig_library { + name: "sensorservice_flags_c_lib", + aconfig_declarations: "sensorservice_flags", + host_supported: true, +} + cc_library { name: "libsensorservice", @@ -70,6 +83,7 @@ cc_library { "android.hardware.sensors@2.1", "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", + "server_configurable_flags", ], static_libs: [ @@ -77,6 +91,7 @@ cc_library { "android.hardware.sensors@1.0-convert", "android.hardware.sensors-V1-convert", "android.hardware.sensors-V2-ndk", + "sensorservice_flags_c_lib", ], generated_headers: ["framework-cppstream-protos"], diff --git a/services/sensorservice/HidlSensorHalWrapper.cpp b/services/sensorservice/HidlSensorHalWrapper.cpp index c55c9b4748..8c867bdd32 100644 --- a/services/sensorservice/HidlSensorHalWrapper.cpp +++ b/services/sensorservice/HidlSensorHalWrapper.cpp @@ -203,6 +203,11 @@ ssize_t HidlSensorHalWrapper::pollFmq(sensors_event_t* buffer, size_t maxNumEven if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) { ALOGD("Event FMQ internal wake, returning from poll with no events"); return DEAD_OBJECT; + } else if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mInHalBypassMode && + availableEvents == 0) { + ALOGD("Event FMQ internal wake due to HAL Bypass Mode, returning from poll with no " + "events"); + return OK; } } @@ -251,6 +256,17 @@ std::vector<sensor_t> HidlSensorHalWrapper::getSensorsList() { status_t HidlSensorHalWrapper::setOperationMode(SensorService::Mode mode) { if (mSensors == nullptr) return NO_INIT; + if (mode == SensorService::Mode::HAL_BYPASS_REPLAY_DATA_INJECTION) { + if (!mInHalBypassMode) { + mInHalBypassMode = true; + mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE)); + } + return OK; + } else { + if (mInHalBypassMode) { + mInHalBypassMode = false; + } + } return checkReturnAndGetStatus( mSensors->setOperationMode(static_cast<hardware::sensors::V1_0::OperationMode>(mode))); } diff --git a/services/sensorservice/ISensorHalWrapper.h b/services/sensorservice/ISensorHalWrapper.h index 3d33540c18..891dfe5a77 100644 --- a/services/sensorservice/ISensorHalWrapper.h +++ b/services/sensorservice/ISensorHalWrapper.h @@ -97,6 +97,8 @@ public: virtual void writeWakeLockHandled(uint32_t count) = 0; std::atomic_bool mReconnecting = false; + + std::atomic_bool mInHalBypassMode = false; }; } // namespace android diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index dd83fdefc3..f62562ce9d 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -16,15 +16,9 @@ #include "SensorDevice.h" -#include "android/hardware/sensors/2.0/types.h" -#include "android/hardware/sensors/2.1/types.h" -#include "convertV2_1.h" - -#include "AidlSensorHalWrapper.h" -#include "HidlSensorHalWrapper.h" - #include <android-base/logging.h> #include <android/util/ProtoOutputStream.h> +#include <com_android_frameworks_sensorservice_flags.h> #include <cutils/atomic.h> #include <frameworks/base/core/proto/android/service/sensor_service.proto.h> #include <hardware/sensors-base.h> @@ -35,13 +29,20 @@ #include <chrono> #include <cinttypes> +#include <condition_variable> #include <cstddef> -#include <thread> #include <mutex> -#include <condition_variable> +#include <thread> + +#include "AidlSensorHalWrapper.h" +#include "HidlSensorHalWrapper.h" +#include "android/hardware/sensors/2.0/types.h" +#include "android/hardware/sensors/2.1/types.h" +#include "convertV2_1.h" using namespace android::hardware::sensors; using android::util::ProtoOutputStream; +namespace sensorservice_flags = com::android::frameworks::sensorservice::flags; namespace android { // --------------------------------------------------------------------------- @@ -166,6 +167,9 @@ void SensorDevice::reconnect() { mActivationCount.clear(); mSensorList.clear(); + if (sensorservice_flags::dynamic_sensor_hal_reconnect_handling()) { + mConnectedDynamicSensors.clear(); + } if (mHalWrapper->connect(this)) { initializeSensorList(); @@ -340,6 +344,15 @@ void SensorDevice::dump(ProtoOutputStream* proto) const { } } +std::vector<int32_t> SensorDevice::getDynamicSensorHandles() { + std::vector<int32_t> sensorHandles; + std::lock_guard<std::mutex> lock(mDynamicSensorsMutex); + for (auto& sensors : mConnectedDynamicSensors) { + sensorHandles.push_back(sensors.first); + } + return sensorHandles; +} + ssize_t SensorDevice::getSensorList(sensor_t const** list) { *list = &mSensorList[0]; @@ -416,8 +429,15 @@ void SensorDevice::onDynamicSensorsConnected(const std::vector<sensor_t>& dynami } void SensorDevice::onDynamicSensorsDisconnected( - const std::vector<int32_t>& /* dynamicSensorHandlesRemoved */) { - // TODO: Currently dynamic sensors do not seem to be removed + const std::vector<int32_t>& dynamicSensorHandlesRemoved) { + if (sensorservice_flags::sensor_device_on_dynamic_sensor_disconnected()) { + for (auto handle : dynamicSensorHandlesRemoved) { + auto it = mConnectedDynamicSensors.find(handle); + if (it != mConnectedDynamicSensors.end()) { + mConnectedDynamicSensors.erase(it); + } + } + } } void SensorDevice::writeWakeLockHandled(uint32_t count) { @@ -483,12 +503,16 @@ status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { } else { ALOGD_IF(DEBUG_CONNECTIONS, "disable index=%zd", info.batchParams.indexOfKey(ident)); - // If a connected dynamic sensor is deactivated, remove it from the - // dictionary. + // TODO(b/316958439): Remove these line after + // sensor_device_on_dynamic_sensor_disconnected is ramped up. Bounded + // here since this function is coupled with + // dynamic_sensors_hal_disconnect_dynamic_sensor flag. If a connected + // dynamic sensor is deactivated, remove it from the dictionary. auto it = mConnectedDynamicSensors.find(handle); if (it != mConnectedDynamicSensors.end()) { - mConnectedDynamicSensors.erase(it); + mConnectedDynamicSensors.erase(it); } + // End of TODO(b/316958439) if (info.removeBatchParamsForIdent(ident) >= 0) { if (info.numActiveClients() == 0) { @@ -788,7 +812,6 @@ status_t SensorDevice::setMode(uint32_t mode) { } mInHalBypassMode = true; } - return OK; } else { if (mInHalBypassMode) { // We are transitioning out of HAL Bypass mode. We need to notify the reader thread diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index f127c0f8cd..52f7cf2de8 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -60,6 +60,8 @@ public: ssize_t getSensorList(sensor_t const** list); + std::vector<int32_t> getDynamicSensorHandles(); + void handleDynamicSensorConnection(int handle, bool connected); status_t initCheck() const; int getHalDeviceVersion() const; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 85043c9038..e1c43c6fec 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "SensorService.h" + #include <aidl/android/hardware/sensors/ISensors.h> #include <android-base/strings.h> #include <android/content/pm/IPackageManagerNative.h> @@ -22,20 +24,36 @@ #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> #include <binder/PermissionController.h> +#include <com_android_frameworks_sensorservice_flags.h> #include <cutils/ashmem.h> #include <cutils/misc.h> #include <cutils/properties.h> #include <frameworks/base/core/proto/android/service/sensor_service.proto.h> #include <hardware/sensors.h> #include <hardware_legacy/power.h> +#include <inttypes.h> #include <log/log.h> +#include <math.h> #include <openssl/digest.h> #include <openssl/hmac.h> #include <openssl/rand.h> +#include <private/android_filesystem_config.h> +#include <sched.h> #include <sensor/SensorEventQueue.h> #include <sensorprivacy/SensorPrivacyManager.h> +#include <stdint.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> #include <utils/SystemClock.h> +#include <condition_variable> +#include <ctime> +#include <future> +#include <mutex> +#include <string> + #include "BatteryService.h" #include "CorrectedGyroSensor.h" #include "GravitySensor.h" @@ -43,35 +61,17 @@ #include "LinearAccelerationSensor.h" #include "OrientationSensor.h" #include "RotationVectorSensor.h" -#include "SensorFusion.h" -#include "SensorInterface.h" - -#include "SensorService.h" #include "SensorDirectConnection.h" #include "SensorEventAckReceiver.h" #include "SensorEventConnection.h" +#include "SensorFusion.h" +#include "SensorInterface.h" #include "SensorRecord.h" #include "SensorRegistrationInfo.h" #include "SensorServiceUtils.h" -#include <inttypes.h> -#include <math.h> -#include <sched.h> -#include <stdint.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <condition_variable> -#include <ctime> -#include <future> -#include <mutex> -#include <string> - -#include <private/android_filesystem_config.h> - using namespace std::chrono_literals; +namespace sensorservice_flags = com::android::frameworks::sensorservice::flags; namespace android { // --------------------------------------------------------------------------- @@ -335,6 +335,11 @@ void SensorService::onFirstRef() { case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: hasGyroUncalibrated = true; break; + case SENSOR_TYPE_DYNAMIC_SENSOR_META: + if (sensorservice_flags::dynamic_sensor_hal_reconnect_handling()) { + mDynamicMetaSensorHandle = list[i].handle; + } + break; case SENSOR_TYPE_GRAVITY: case SENSOR_TYPE_LINEAR_ACCELERATION: case SENSOR_TYPE_ROTATION_VECTOR: @@ -1055,6 +1060,68 @@ void SensorService::cleanupAutoDisabledSensorLocked(const sp<SensorEventConnecti } } +void SensorService::sendEventsToAllClients( + const std::vector<sp<SensorEventConnection>>& activeConnections, + ssize_t count) { + // Send our events to clients. Check the state of wake lock for each client + // and release the lock if none of the clients need it. + bool needsWakeLock = false; + for (const sp<SensorEventConnection>& connection : activeConnections) { + connection->sendEvents(mSensorEventBuffer, count, mSensorEventScratch, + mMapFlushEventsToConnections); + needsWakeLock |= connection->needsWakeLock(); + // If the connection has one-shot sensors, it may be cleaned up after + // first trigger. Early check for one-shot sensors. + if (connection->hasOneShotSensors()) { + cleanupAutoDisabledSensorLocked(connection, mSensorEventBuffer, count); + } + } + + if (mWakeLockAcquired && !needsWakeLock) { + setWakeLockAcquiredLocked(false); + } +} + +void SensorService::disconnectDynamicSensor( + int handle, + const std::vector<sp<SensorEventConnection>>& activeConnections) { + ALOGI("Dynamic sensor handle 0x%x disconnected", handle); + SensorDevice::getInstance().handleDynamicSensorConnection( + handle, false /*connected*/); + if (!unregisterDynamicSensorLocked(handle)) { + ALOGE("Dynamic sensor release error."); + } + for (const sp<SensorEventConnection>& connection : activeConnections) { + connection->removeSensor(handle); + } +} + +void SensorService::handleDeviceReconnection(SensorDevice& device) { + if (sensorservice_flags::dynamic_sensor_hal_reconnect_handling()) { + const std::vector<sp<SensorEventConnection>> activeConnections = + mConnectionHolder.lock(mLock).getActiveConnections(); + + for (int32_t handle : device.getDynamicSensorHandles()) { + if (mDynamicMetaSensorHandle.has_value()) { + // Sending one event at a time to prevent the number of handle is more than the + // buffer can hold. + mSensorEventBuffer[0].type = SENSOR_TYPE_DYNAMIC_SENSOR_META; + mSensorEventBuffer[0].sensor = *mDynamicMetaSensorHandle; + mSensorEventBuffer[0].dynamic_sensor_meta.connected = false; + mSensorEventBuffer[0].dynamic_sensor_meta.handle = handle; + mMapFlushEventsToConnections[0] = nullptr; + + disconnectDynamicSensor(handle, activeConnections); + sendEventsToAllClients(activeConnections, 1); + } else { + ALOGE("Failed to find mDynamicMetaSensorHandle during init."); + break; + } + } + } + device.reconnect(); +} + bool SensorService::threadLoop() { ALOGD("nuSensorService thread starting..."); @@ -1071,8 +1138,8 @@ bool SensorService::threadLoop() { do { ssize_t count = device.poll(mSensorEventBuffer, numEventMax); if (count < 0) { - if(count == DEAD_OBJECT && device.isReconnecting()) { - device.reconnect(); + if (count == DEAD_OBJECT && device.isReconnecting()) { + handleDeviceReconnection(device); continue; } else { ALOGE("sensor poll failed (%s)", strerror(-count)); @@ -1176,7 +1243,6 @@ bool SensorService::threadLoop() { rec->removeFirstPendingFlushConnection(); } } - // handle dynamic sensor meta events, process registration and unregistration of dynamic // sensor based on content of event. if (mSensorEventBuffer[i].type == SENSOR_TYPE_DYNAMIC_SENSOR_META) { @@ -1206,37 +1272,14 @@ bool SensorService::threadLoop() { } } else { int handle = mSensorEventBuffer[i].dynamic_sensor_meta.handle; - ALOGI("Dynamic sensor handle 0x%x disconnected", handle); - - device.handleDynamicSensorConnection(handle, false /*connected*/); - if (!unregisterDynamicSensorLocked(handle)) { - ALOGE("Dynamic sensor release error."); - } - - for (const sp<SensorEventConnection>& connection : activeConnections) { - connection->removeSensor(handle); - } + disconnectDynamicSensor(handle, activeConnections); } } } // Send our events to clients. Check the state of wake lock for each client and release the // lock if none of the clients need it. - bool needsWakeLock = false; - for (const sp<SensorEventConnection>& connection : activeConnections) { - connection->sendEvents(mSensorEventBuffer, count, mSensorEventScratch, - mMapFlushEventsToConnections); - needsWakeLock |= connection->needsWakeLock(); - // If the connection has one-shot sensors, it may be cleaned up after first trigger. - // Early check for one-shot sensors. - if (connection->hasOneShotSensors()) { - cleanupAutoDisabledSensorLocked(connection, mSensorEventBuffer, count); - } - } - - if (mWakeLockAcquired && !needsWakeLock) { - setWakeLockAcquiredLocked(false); - } + sendEventsToAllClients(activeConnections, count); } while (!Thread::exitPending()); ALOGW("Exiting SensorService::threadLoop => aborting..."); diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index b643f6b382..118d9281fc 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -17,9 +17,6 @@ #ifndef ANDROID_SENSOR_SERVICE_H #define ANDROID_SENSOR_SERVICE_H -#include "SensorList.h" -#include "RecentEventLogger.h" - #include <android-base/macros.h> #include <binder/AppOpsManager.h> #include <binder/BinderService.h> @@ -27,11 +24,11 @@ #include <cutils/compiler.h> #include <cutils/multiuser.h> #include <private/android_filesystem_config.h> -#include <sensor/ISensorServer.h> #include <sensor/ISensorEventConnection.h> +#include <sensor/ISensorServer.h> #include <sensor/Sensor.h> -#include "android/hardware/BnSensorPrivacyListener.h" - +#include <stdint.h> +#include <sys/types.h> #include <utils/AndroidThreads.h> #include <utils/KeyedVector.h> #include <utils/Looper.h> @@ -40,8 +37,6 @@ #include <utils/Vector.h> #include <utils/threads.h> -#include <stdint.h> -#include <sys/types.h> #include <condition_variable> #include <mutex> #include <queue> @@ -49,6 +44,10 @@ #include <unordered_set> #include <vector> +#include "RecentEventLogger.h" +#include "SensorList.h" +#include "android/hardware/BnSensorPrivacyListener.h" + #if __clang__ // Clang warns about SensorEventConnection::dump hiding BBinder::dump. The cause isn't fixable // without changing the API, so let's tell clang this is indeed intentional. @@ -57,7 +56,7 @@ // --------------------------------------------------------------------------- #define IGNORE_HARDWARE_FUSION false -#define DEBUG_CONNECTIONS false +#define DEBUG_CONNECTIONS false // Max size is 100 KB which is enough to accept a batch of about 1000 events. #define MAX_SOCKET_BUFFER_SIZE_BATCHED (100 * 1024) // For older HALs which don't support batching, use a smaller socket buffer size. @@ -341,6 +340,12 @@ private: binder::Status onSensorPrivacyChanged(int toggleType, int sensor, bool enabled); + // This callback is used for additional automotive-specific states for sensor privacy + // such as AUTO_DRIVER_ASSISTANCE_APPS. The newly defined states will only be valid + // for camera privacy on automotive devices. onSensorPrivacyChanged() will still be + // invoked whenever the enabled status of a toggle changes. + binder::Status onSensorPrivacyStateChanged(int, int, int) {return binder::Status::ok();} + protected: std::atomic_bool mSensorPrivacyEnabled; wp<SensorService> mService; @@ -453,6 +458,11 @@ private: // Send events from the event cache for this particular connection. void sendEventsFromCache(const sp<SensorEventConnection>& connection); + // Send all events in the buffer to all clients. + void sendEventsToAllClients( + const std::vector<sp<SensorEventConnection>>& activeConnections, + ssize_t count); + // If SensorService is operating in RESTRICTED mode, only select whitelisted packages are // allowed to register for or call flush on sensors. Typically only cts test packages are // allowed. @@ -516,6 +526,14 @@ private: bool isInjectionMode(int mode); + void handleDeviceReconnection(SensorDevice& device); + + // Removes a connected dynamic sensor and send the corresponding event to + // all connections. + void disconnectDynamicSensor( + int handle, + const std::vector<sp<SensorEventConnection>>& activeConnections); + static inline bool isAudioServerOrSystemServerUid(uid_t uid) { return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER; } @@ -583,6 +601,10 @@ private: bool mLastReportedProxIsActive; // Listeners subscribed to receive updates on the proximity sensor active state. std::vector<sp<ProximityActiveListener>> mProximityActiveListeners; + + // Stores the handle of the dynamic_meta sensor to send clean up event once + // HAL crashes. + std::optional<int> mDynamicMetaSensorHandle; }; } // namespace android diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig new file mode 100644 index 0000000000..8d43f79950 --- /dev/null +++ b/services/sensorservice/senserservice_flags.aconfig @@ -0,0 +1,16 @@ +package: "com.android.frameworks.sensorservice.flags" +container: "system" + +flag { + name: "dynamic_sensor_hal_reconnect_handling" + namespace: "sensors" + description: "This flag controls if the dynamic sensor data will be clean up after HAL is disconnected." + bug: "307782607" +} + +flag { + name: "sensor_device_on_dynamic_sensor_disconnected" + namespace: "sensors" + description: "This flag controls if the callback onDynamicSensorsDisconnected is implemented or not." + bug: "316958439" +}
\ No newline at end of file diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index dcef9a3775..46252e139f 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -5,13 +5,17 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } aconfig_declarations { name: "surfaceflinger_flags", package: "com.android.graphics.surfaceflinger.flags", container: "system", - srcs: ["surfaceflinger_flags.aconfig"], + srcs: [ + "surfaceflinger_flags.aconfig", + "surfaceflinger_flags_new.aconfig", + ], } cc_aconfig_library { diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 0a70d4299d..dad945c964 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h index 1a235e9401..18a96f4de7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -24,6 +24,7 @@ #include <compositionengine/LayerFE.h> #include <compositionengine/OutputColorSetting.h> #include <math/mat4.h> +#include <scheduler/interface/ICompositor.h> #include <ui/FenceTime.h> #include <ui/Transform.h> @@ -89,17 +90,14 @@ struct CompositionRefreshArgs { // If set, causes the dirty regions to flash with the delay std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay; - // Optional. - // The earliest time to send the present command to the HAL. - std::optional<std::chrono::steady_clock::time_point> earliestPresentTime; - - // The expected time for the next present - nsecs_t expectedPresentTime{0}; + scheduler::FrameTargets frameTargets; // The frameInterval for the next present - Fps frameInterval{}; + // TODO (b/315371484): Calculate per display and store on `FrameTarget`. + Fps frameInterval; // If set, a frame has been scheduled for that time. + // TODO (b/255601557): Calculate per display. std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime; std::vector<BorderRenderInfo> borderInfoList; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h index 5e84be1841..39748b8417 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -40,6 +40,9 @@ public: // True if the display is secure virtual bool isSecure() const = 0; + // Sets the secure flag for the display + virtual void setSecure(bool secure) = 0; + // True if the display is virtual virtual bool isVirtual() const = 0; @@ -57,7 +60,7 @@ public: virtual void createClientCompositionCache(uint32_t cacheSize) = 0; // Sends the brightness setting to HWC - virtual void applyDisplayBrightness(const bool applyImmediately) = 0; + virtual void applyDisplayBrightness(bool applyImmediately) = 0; protected: ~Display() = default; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index eac5d97df3..2dc9a1a49a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -73,7 +73,8 @@ public: const compositionengine::DisplayColorProfileCreationArgs&) override; void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override; void createClientCompositionCache(uint32_t cacheSize) override; - void applyDisplayBrightness(const bool applyImmediately) override; + void applyDisplayBrightness(bool applyImmediately) override; + void setSecure(bool secure) override; // Internal helpers used by chooseCompositionStrategy() using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index d26ca9dd00..e2d17ee502 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -147,8 +147,6 @@ public: bool hasProtectedLayers() const; - bool hasSolidColorLayers() const; - // True if any layer in this cached set has CachingHint::Disabled bool cachingHintExcludesLayers() const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h index 1f241b091c..dc3821ca43 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h @@ -258,11 +258,6 @@ public: gui::CachingHint getCachingHint() const { return mCachingHint.get(); } - bool hasSolidColorCompositionType() const { - return getOutputLayer()->getLayerFE().getCompositionState()->compositionType == - aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR; - } - float getFps() const { return getOutputLayer()->getLayerFE().getCompositionState()->fps; } void dump(std::string& result) const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h index 7e99ec2f5a..46cb95ef25 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -33,6 +33,7 @@ public: MOCK_CONST_METHOD0(getId, DisplayId()); MOCK_CONST_METHOD0(isSecure, bool()); + MOCK_METHOD1(setSecure, void(bool)); MOCK_CONST_METHOD0(isVirtual, bool()); MOCK_CONST_METHOD0(getPreferredBootHwcConfigId, int32_t()); diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 7a727fdb83..6428d089a7 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -74,6 +74,10 @@ bool Display::isSecure() const { return getState().isSecure; } +void Display::setSecure(bool secure) { + editState().isSecure = secure; +} + bool Display::isVirtual() const { return VirtualDisplayId::tryCast(mId).has_value(); } @@ -200,13 +204,12 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& setReleasedLayers(std::move(releasedLayers)); } -void Display::applyDisplayBrightness(const bool applyImmediately) { - auto& hwc = getCompositionEngine().getHwComposer(); - const auto halDisplayId = HalDisplayId::tryCast(*getDisplayId()); - if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId); - physicalDisplayId && getState().displayBrightness) { +void Display::applyDisplayBrightness(bool applyImmediately) { + if (const auto displayId = ftl::Optional(getDisplayId()).and_then(PhysicalDisplayId::tryCast); + displayId && getState().displayBrightness) { + auto& hwc = getCompositionEngine().getHwComposer(); const status_t result = - hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness, + hwc.setDisplayBrightness(*displayId, *getState().displayBrightness, getState().displayBrightnessNits, Hwc2::Composer::DisplayBrightnessOptions{ .applyImmediately = applyImmediately}) diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 55db5b67a2..921e05dfb2 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -28,8 +28,11 @@ #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/impl/planner/Planner.h> +#include <ftl/algorithm.h> #include <ftl/future.h> #include <gui/TraceUtils.h> +#include <scheduler/FrameTargeter.h> +#include <scheduler/Time.h> #include <optional> #include <thread> @@ -429,7 +432,28 @@ void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArg ftl::Future<std::monostate> Output::present( const compositionengine::CompositionRefreshArgs& refreshArgs) { - ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str()); + const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string { + return ftl::Optional(getDisplayId()) + .and_then(PhysicalDisplayId::tryCast) + .and_then([&refreshArgs](PhysicalDisplayId id) { + return refreshArgs.frameTargets.get(id); + }) + .transform([](const auto& frameTargetPtr) { + return frameTargetPtr.get()->expectedPresentTime(); + }) + .transform([](TimePoint expectedPresentTime) { + return base::StringPrintf(" vsyncIn %.2fms", + ticks<std::milli, float>(expectedPresentTime - + TimePoint::now())); + }) + .or_else([] { + // There is no vsync for this output. + return std::make_optional(std::string()); + }) + .value(); + }; + ATRACE_FORMAT("%s for %s%s", __func__, mNamePlusId.c_str(), + stringifyExpectedPresentTime().c_str()); ALOGV(__FUNCTION__); updateColorProfile(refreshArgs); @@ -853,8 +877,14 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr return; } - editState().earliestPresentTime = refreshArgs.earliestPresentTime; - editState().expectedPresentTime = refreshArgs.expectedPresentTime; + if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId()) + .and_then(PhysicalDisplayId::tryCast) + .and_then([&refreshArgs](PhysicalDisplayId id) { + return refreshArgs.frameTargets.get(id); + })) { + editState().earliestPresentTime = frameTargetPtrOpt->get()->earliestPresentTime(); + editState().expectedPresentTime = frameTargetPtrOpt->get()->expectedPresentTime().ns(); + } editState().frameInterval = refreshArgs.frameInterval; editState().powerCallback = refreshArgs.powerCallback; @@ -1246,8 +1276,9 @@ void Output::updateProtectedContentState() { if (isProtected && supportsProtectedContent) { auto layers = getOutputLayersOrderedByZ(); bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) { - return layer->getLayerFE().getCompositionState()->hasProtectedContent - && layer->requiresClientComposition(); + return layer->getLayerFE().getCompositionState()->hasProtectedContent && + (!FlagManager::getInstance().protected_if_client() || + layer->requiresClientComposition()); }); if (needsProtected != mRenderSurface->isProtected()) { mRenderSurface->setProtected(needsProtected); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 7fe3369f88..091c207464 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -394,7 +394,6 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t auto requestedCompositionType = outputIndependentState->compositionType; if (requestedCompositionType == Composition::SOLID_COLOR && state.overrideInfo.buffer) { - // this should never happen, as SOLID_COLOR is skipped from caching, b/230073351 requestedCompositionType = Composition::DEVICE; } @@ -665,6 +664,9 @@ void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState, bool skipLayer) { + if (skipLayer && outputIndependentState.buffer == nullptr) { + return; + } auto supportedPerFrameMetadata = getOutput().getDisplayColorProfile()->getSupportedPerFrameMetadata(); if (auto error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index 869dda61bb..1f53588412 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -393,12 +393,6 @@ bool CachedSet::hasProtectedLayers() const { [](const Layer& layer) { return layer.getState()->isProtected(); }); } -bool CachedSet::hasSolidColorLayers() const { - return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) { - return layer.getState()->hasSolidColorCompositionType(); - }); -} - bool CachedSet::cachingHintExcludesLayers() const { const bool shouldExcludeLayers = std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index a18397d8ba..0a5c43a99b 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -56,7 +56,7 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers, } // Do not unflatten if source crop is only moved. - if (FlagManager::getInstance().cache_if_source_crop_layer_only_moved() && + if (FlagManager::getInstance().cache_when_source_crop_layer_only_moved() && incomingLayers[i]->isSourceCropSizeEqual(*(existingLayers[i])) && incomingLayers[i]->getDifferingFields(*(existingLayers[i])) == LayerStateField::SourceCrop) { @@ -513,13 +513,6 @@ void Flattener::buildCachedSets(time_point now) { } } - for (const CachedSet& layer : mLayers) { - if (layer.hasSolidColorLayers()) { - ATRACE_NAME("layer->hasSolidColorLayers()"); - return; - } - } - std::vector<Run> runs = findCandidateRuns(now); std::optional<Run> bestRun = findBestRun(runs); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 9e35717c95..575a30e522 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -97,13 +97,13 @@ public: MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId)); MOCK_CONST_METHOD2(getModes, std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t)); - MOCK_CONST_METHOD1(getActiveMode, std::optional<hal::HWConfigId>(PhysicalDisplayId)); + MOCK_CONST_METHOD1(getActiveMode, ftl::Expected<hal::HWConfigId, status_t>(PhysicalDisplayId)); MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId)); MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent)); MOCK_CONST_METHOD0(isUsingVrComposer, bool()); MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId)); MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId)); - MOCK_CONST_METHOD2(getDisplayVsyncPeriod, status_t(PhysicalDisplayId, nsecs_t*)); + MOCK_CONST_METHOD1(getDisplayVsyncPeriod, ftl::Expected<nsecs_t, status_t>(PhysicalDisplayId)); MOCK_METHOD4(setActiveModeWithConstraints, status_t(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 97a2de0a17..799c7edebd 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -4018,7 +4018,6 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, usesExpectedDisplaySettingsWithFp16Buffer) { SET_FLAG_FOR_TEST(flags::fp16_client_target, true); - ALOGE("alecmouri: %d", flags::fp16_client_target()); verify().ifMixedCompositionIs(false) .andIfUsesHdr(true) .withDisplayBrightnessNits(kDisplayLuminance) @@ -4093,7 +4092,12 @@ struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeS }; TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) { - mOutput.mState.isSecure = true; + SET_FLAG_FOR_TEST(flags::protected_if_client, true); + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer2.mLayerFEState.hasProtectedContent = false; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); @@ -4107,7 +4111,12 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLa } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { - mOutput.mState.isSecure = true; + SET_FLAG_FOR_TEST(flags::protected_if_client, true); + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); @@ -4129,7 +4138,12 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) { - mOutput.mState.isSecure = true; + SET_FLAG_FOR_TEST(flags::protected_if_client, true); + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); @@ -4142,7 +4156,12 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEveryw } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) { - mOutput.mState.isSecure = true; + SET_FLAG_FOR_TEST(flags::protected_if_client, true); + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); @@ -4221,6 +4240,7 @@ struct GenerateClientCompositionRequestsTest : public testing::Test { GenerateClientCompositionRequestsTest() { mOutput.mState.needsFiltering = false; + mOutput.mState.isProtected = true; mOutput.setDisplayColorProfileForTest( std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); @@ -4245,6 +4265,7 @@ struct GenerateClientCompositionRequestsTest_ThreeLayers mOutput.mState.displaySpace.setOrientation(kDisplayOrientation); mOutput.mState.needsFiltering = false; mOutput.mState.isSecure = false; + mOutput.mState.isProtected = true; for (size_t i = 0; i < mLayers.size(); i++) { mLayers[i].mOutputLayerState.clearClientTarget = false; @@ -4707,7 +4728,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4720,7 +4741,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4733,7 +4754,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4911,7 +4932,7 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq Region(Rect(0, 0, 1000, 1000)), false, /* needs filtering */ true, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, @@ -4930,7 +4951,7 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq Region(Rect(1000, 0, 2000, 1000)), false, /* needs filtering */ true, /* secure */ - true, /* supports protected content */ + true, /* isProtected */ kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, @@ -5122,7 +5143,12 @@ struct OutputUpdateProtectedContentStateTest : public testing::Test { }; TEST_F(OutputUpdateProtectedContentStateTest, ifProtectedContentLayerComposeByHWC) { - mOutput.mState.isSecure = true; + SET_FLAG_FOR_TEST(flags::protected_if_client, true); + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer1.mLayerFEState.hasProtectedContent = false; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); @@ -5133,7 +5159,12 @@ TEST_F(OutputUpdateProtectedContentStateTest, ifProtectedContentLayerComposeByHW } TEST_F(OutputUpdateProtectedContentStateTest, ifProtectedContentLayerComposeByClient) { - mOutput.mState.isSecure = true; + SET_FLAG_FOR_TEST(flags::protected_if_client, true); + if (FlagManager::getInstance().display_protected()) { + mOutput.mState.isProtected = true; + } else { + mOutput.mState.isSecure = true; + } mLayer1.mLayerFEState.hasProtectedContent = false; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 763b998b3d..d2eff75fb6 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -244,7 +244,7 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) { TEST_F(FlattenerTest, unflattenLayers_onlySourceCropMoved) { SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags:: - cache_if_source_crop_layer_only_moved, + cache_when_source_crop_layer_only_moved, true); auto& layerState1 = mTestLayers[0]->layerState; diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h index d07cdf55d2..ec3ec526a1 100644 --- a/services/surfaceflinger/Display/DisplayModeRequest.h +++ b/services/surfaceflinger/Display/DisplayModeRequest.h @@ -16,6 +16,7 @@ #pragma once +#include <android-base/stringprintf.h> #include <ftl/non_null.h> #include <scheduler/FrameRateMode.h> @@ -27,10 +28,19 @@ struct DisplayModeRequest { // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE. bool emitEvent = false; + + // Whether to force the request to be applied, even if the mode is unchanged. + bool force = false; }; inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) { return lhs.mode == rhs.mode && lhs.emitEvent == rhs.emitEvent; } +inline std::string to_string(const DisplayModeRequest& request) { + constexpr const char* kBool[] = {"false", "true"}; + return base::StringPrintf("{mode=%s, emitEvent=%s, force=%s}", to_string(request.mode).c_str(), + kBool[request.emitEvent], kBool[request.force]); +} + } // namespace android::display diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 950b05e1da..62f8fb16f0 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -24,6 +24,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/FlagManager.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> @@ -67,6 +68,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mActiveModeFpsTrace(concatId("ActiveModeFps")), mRenderRateFpsTrace(concatId("RenderRateFps")), mPhysicalOrientation(args.physicalOrientation), + mPowerMode(ftl::Concat("PowerMode ", getId().value).c_str(), args.initialPowerMode), mIsPrimary(args.isPrimary), mRequestedRefreshRate(args.requestedRefreshRate), mRefreshRateSelector(std::move(args.refreshRateSelector)), @@ -105,9 +107,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mCompositionDisplay->getRenderSurface()->initialize(); - if (const auto powerModeOpt = args.initialPowerMode) { - setPowerMode(*powerModeOpt); - } + setPowerMode(args.initialPowerMode); // initialize the display orientation transform. setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT); @@ -172,6 +172,7 @@ auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo { } void DisplayDevice::setPowerMode(hal::PowerMode mode) { + // TODO(b/241285876): Skip this for virtual displays. if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) { if (mStagedBrightness && mBrightness != mStagedBrightness) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); @@ -181,33 +182,26 @@ void DisplayDevice::setPowerMode(hal::PowerMode mode) { getCompositionDisplay()->applyDisplayBrightness(true); } - if (mPowerMode) { - *mPowerMode = mode; - } else { - mPowerMode.emplace("PowerMode -" + to_string(getId()), mode); - } + mPowerMode = mode; getCompositionDisplay()->setCompositionEnabled(isPoweredOn()); } void DisplayDevice::tracePowerMode() { - // assign the same value for tracing - if (mPowerMode) { - const hal::PowerMode powerMode = *mPowerMode; - *mPowerMode = powerMode; - } + // Assign the same value for tracing. + mPowerMode = mPowerMode.get(); } void DisplayDevice::enableLayerCaching(bool enable) { getCompositionDisplay()->setLayerCachingEnabled(enable); } -std::optional<hal::PowerMode> DisplayDevice::getPowerMode() const { +hal::PowerMode DisplayDevice::getPowerMode() const { return mPowerMode; } bool DisplayDevice::isPoweredOn() const { - return mPowerMode && *mPowerMode != hal::PowerMode::OFF; + return mPowerMode != hal::PowerMode::OFF; } void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) { @@ -221,6 +215,17 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps rende bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline& outTimeline) { + // TODO(b/255635711): Flow the DisplayModeRequest through the desired/pending/active states. For + // now, `desiredMode` and `mDesiredModeOpt` are one and the same, but the latter is not cleared + // until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been consumed + // at this point, so clear the `force` flag to prevent an endless loop of `initiateModeChange`. + if (FlagManager::getInstance().connected_display()) { + std::scoped_lock lock(mDesiredModeLock); + if (mDesiredModeOpt) { + mDesiredModeOpt->force = false; + } + } + mPendingModeOpt = std::move(desiredMode); mIsModeSetPending = true; @@ -246,10 +251,8 @@ nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { return 0; } - nsecs_t vsyncPeriod; - const auto status = mHwComposer.getDisplayVsyncPeriod(physicalId, &vsyncPeriod); - if (status == NO_ERROR) { - return vsyncPeriod; + if (const auto vsyncPeriodOpt = mHwComposer.getDisplayVsyncPeriod(physicalId).value_opt()) { + return *vsyncPeriodOpt; } return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs(); @@ -353,6 +356,10 @@ bool DisplayDevice::isSecure() const { return mCompositionDisplay->isSecure(); } +void DisplayDevice::setSecure(bool secure) { + mCompositionDisplay->setSecure(secure); +} + const Rect DisplayDevice::getBounds() const { return mCompositionDisplay->getState().displaySpace.getBoundsAsRect(); } @@ -522,29 +529,27 @@ void DisplayDevice::animateOverlay() { } } -auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force) - -> DesiredModeAction { - ATRACE_CALL(); - - const auto& desiredModePtr = desiredMode.mode.modePtr; - - LOG_ALWAYS_FATAL_IF(getPhysicalId() != desiredModePtr->getPhysicalDisplayId(), - "DisplayId mismatch"); - - ALOGV("%s(%s)", __func__, to_string(*desiredModePtr).c_str()); +auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode) -> DesiredModeAction { + ATRACE_NAME(concatId(__func__).c_str()); + ALOGD("%s %s", concatId(__func__).c_str(), to_string(desiredMode).c_str()); std::scoped_lock lock(mDesiredModeLock); if (mDesiredModeOpt) { // A mode transition was already scheduled, so just override the desired mode. const bool emitEvent = mDesiredModeOpt->emitEvent; + const bool force = mDesiredModeOpt->force; mDesiredModeOpt = std::move(desiredMode); mDesiredModeOpt->emitEvent |= emitEvent; + if (FlagManager::getInstance().connected_display()) { + mDesiredModeOpt->force |= force; + } return DesiredModeAction::None; } // If the desired mode is already active... const auto activeMode = refreshRateSelector().getActiveMode(); - if (!force && activeMode.modePtr->getId() == desiredModePtr->getId()) { + if (const auto& desiredModePtr = desiredMode.mode.modePtr; + !desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) { if (activeMode == desiredMode.mode) { return DesiredModeAction::None; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index ac390cb8ff..edd57cce91 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -95,6 +95,7 @@ public: // isSecure indicates whether this display can be trusted to display // secure surfaces. bool isSecure() const; + void setSecure(bool secure); int getWidth() const; int getHeight() const; @@ -172,8 +173,8 @@ public: /* ------------------------------------------------------------------------ * Display power mode management. */ - std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const; - void setPowerMode(hardware::graphics::composer::hal::PowerMode mode); + hardware::graphics::composer::hal::PowerMode getPowerMode() const; + void setPowerMode(hardware::graphics::composer::hal::PowerMode); bool isPoweredOn() const; void tracePowerMode(); @@ -188,8 +189,7 @@ public: enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch }; - DesiredModeAction setDesiredMode(display::DisplayModeRequest&&, bool force = false) - EXCLUDES(mDesiredModeLock); + DesiredModeAction setDesiredMode(display::DisplayModeRequest&&) EXCLUDES(mDesiredModeLock); using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>; @@ -270,9 +270,7 @@ private: ui::Rotation mOrientation = ui::ROTATION_0; bool mIsOrientationChanged = false; - // Allow nullopt as initial power mode. - using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>; - std::optional<TracedPowerMode> mPowerMode; + TracedOrdinal<hardware::graphics::composer::hal::PowerMode> mPowerMode; std::optional<float> mStagedBrightness; std::optional<float> mBrightness; @@ -362,7 +360,8 @@ struct DisplayDeviceCreationArgs { HdrCapabilities hdrCapabilities; int32_t supportedPerFrameMetadata{0}; std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes; - std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode; + hardware::graphics::composer::hal::PowerMode initialPowerMode{ + hardware::graphics::composer::hal::PowerMode::OFF}; bool isPrimary{false}; DisplayModeId activeModeId; // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 6250b1155e..362ab9c39e 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -330,7 +330,11 @@ std::string AidlComposer::dumpDebugInfo() { t.join(); close(pipefds[0]); - return str; + + std::string hash; + mAidlComposer->getInterfaceHash(&hash); + return std::string(mAidlComposer->descriptor) + + " version:" + std::to_string(mComposerInterfaceVersion) + " hash:" + hash + str; } void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h index ba0825c5af..224f50e78e 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayMode.h +++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h @@ -21,17 +21,17 @@ #include <android-base/stringprintf.h> #include <android/configuration.h> +#include <ftl/mixins.h> #include <ftl/small_map.h> #include <ui/DisplayId.h> #include <ui/DisplayMode.h> #include <ui/Size.h> #include <utils/Timers.h> +#include <common/FlagManager.h> #include <scheduler/Fps.h> -#include <common/FlagManager.h> #include "DisplayHardware/Hal.h" -#include "Scheduler/StrongTyping.h" namespace android { @@ -46,7 +46,12 @@ bool operator>(const DisplayModePtr&, const DisplayModePtr&) = delete; bool operator<=(const DisplayModePtr&, const DisplayModePtr&) = delete; bool operator>=(const DisplayModePtr&, const DisplayModePtr&) = delete; -using DisplayModeId = StrongTyping<ui::DisplayModeId, struct DisplayModeIdTag, Compare>; +struct DisplayModeId : ftl::DefaultConstructible<DisplayModeId, ui::DisplayModeId>, + ftl::Incrementable<DisplayModeId>, + ftl::Equatable<DisplayModeId>, + ftl::Orderable<DisplayModeId> { + using DefaultConstructible::DefaultConstructible; +}; using DisplayModes = ftl::SmallMap<DisplayModeId, DisplayModePtr, 3>; using DisplayModeIterator = DisplayModes::const_iterator; @@ -185,7 +190,7 @@ inline bool equalsExceptDisplayModeId(const DisplayMode& lhs, const DisplayMode& inline std::string to_string(const DisplayMode& mode) { return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, vsyncRate=%s, " "dpi=%.2fx%.2f, group=%d, vrrConfig=%s}", - mode.getId().value(), mode.getHwcId(), mode.getWidth(), + ftl::to_underlying(mode.getId()), mode.getHwcId(), mode.getWidth(), mode.getHeight(), to_string(mode.getVsyncRate()).c_str(), mode.getDpi().x, mode.getDpi().y, mode.getGroup(), to_string(mode.getVrrConfig()).c_str()); diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 704ece516d..84f668d9df 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -27,6 +27,7 @@ #include "HWC2.h" #include <android/configuration.h> +#include <common/FlagManager.h> #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> @@ -281,19 +282,28 @@ Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests, return Error::NONE; } -Error Display::getConnectionType(ui::DisplayConnectionType* outType) const { - if (mType != DisplayType::PHYSICAL) return Error::BAD_DISPLAY; +ftl::Expected<ui::DisplayConnectionType, hal::Error> Display::getConnectionType() const { + if (!mConnectionType) { + mConnectionType = [this]() -> decltype(mConnectionType) { + if (mType != DisplayType::PHYSICAL) { + return ftl::Unexpected(Error::BAD_DISPLAY); + } - using ConnectionType = Hwc2::IComposerClient::DisplayConnectionType; - ConnectionType connectionType; - const auto error = static_cast<Error>(mComposer.getDisplayConnectionType(mId, &connectionType)); - if (error != Error::NONE) { - return error; + using ConnectionType = Hwc2::IComposerClient::DisplayConnectionType; + ConnectionType connectionType; + + if (const auto error = static_cast<Error>( + mComposer.getDisplayConnectionType(mId, &connectionType)); + error != Error::NONE) { + return ftl::Unexpected(error); + } + + return connectionType == ConnectionType::INTERNAL ? ui::DisplayConnectionType::Internal + : ui::DisplayConnectionType::External; + }(); } - *outType = connectionType == ConnectionType::INTERNAL ? ui::DisplayConnectionType::Internal - : ui::DisplayConnectionType::External; - return Error::NONE; + return *mConnectionType; } bool Display::hasCapability(DisplayCapability capability) const { @@ -416,7 +426,14 @@ Error Display::setActiveConfigWithConstraints(hal::HWConfigId configId, VsyncPeriodChangeTimeline* outTimeline) { ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId); - if (isVsyncPeriodSwitchSupported()) { + // FIXME (b/319505580): At least the first config set on an external display must be + // `setActiveConfig`, so skip over the block that calls `setActiveConfigWithConstraints` + // for simplicity. + const bool connected_display = FlagManager::getInstance().connected_display(); + + if (isVsyncPeriodSwitchSupported() && + (!connected_display || + getConnectionType().value_opt() != ui::DisplayConnectionType::External)) { Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints; hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos; hwc2Constraints.seamlessRequired = constraints.seamlessRequired; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index f907061774..de044e0b76 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -18,6 +18,7 @@ #include <android-base/expected.h> #include <android-base/thread_annotations.h> +#include <ftl/expected.h> #include <ftl/future.h> #include <gui/HdrMetadata.h> #include <math/mat4.h> @@ -120,7 +121,8 @@ public: [[nodiscard]] virtual hal::Error getRequests( hal::DisplayRequest* outDisplayRequests, std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) = 0; - [[nodiscard]] virtual hal::Error getConnectionType(ui::DisplayConnectionType*) const = 0; + [[nodiscard]] virtual ftl::Expected<ui::DisplayConnectionType, hal::Error> getConnectionType() + const = 0; [[nodiscard]] virtual hal::Error supportsDoze(bool* outSupport) const = 0; [[nodiscard]] virtual hal::Error getHdrCapabilities( android::HdrCapabilities* outCapabilities) const = 0; @@ -213,7 +215,7 @@ public: hal::Error getRequests( hal::DisplayRequest* outDisplayRequests, std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override; - hal::Error getConnectionType(ui::DisplayConnectionType*) const override; + ftl::Expected<ui::DisplayConnectionType, hal::Error> getConnectionType() const override; hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex); hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override; hal::Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties* @@ -294,6 +296,8 @@ private: const hal::HWDisplayId mId; hal::DisplayType mType; + // Cached on first call to getConnectionType. + mutable std::optional<ftl::Expected<ui::DisplayConnectionType, hal::Error>> mConnectionType; bool mIsConnected = false; using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 3ffd8ea316..776bcd3e32 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -77,6 +77,8 @@ using aidl::android::hardware::graphics::common::HdrConversionCapability; using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::VrrConfig; +using namespace std::string_literals; namespace hal = android::hardware::graphics::composer::hal; namespace android { @@ -89,7 +91,8 @@ HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)), mMaxVirtualDisplayDimension(static_cast<size_t>(sysprop::max_virtual_display_dimension(0))), mUpdateDeviceProductInfoOnHotplugReconnect( - sysprop::update_device_product_info_on_hotplug_reconnect(false)) {} + sysprop::update_device_product_info_on_hotplug_reconnect(false)), + mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, true)) {} HWComposer::HWComposer(const std::string& composerServiceName) : HWComposer(Hwc2::Composer::create(composerServiceName)) {} @@ -299,6 +302,10 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigura hwcMode.dpiY = config.dpi->y; } + if (!mEnableVrrTimeout) { + hwcMode.vrrConfig->notifyExpectedPresentConfig = {}; + } + modes.push_back(hwcMode); } @@ -336,14 +343,18 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayCon return modes; } -std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displayId) const { - RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt); +ftl::Expected<hal::HWConfigId, status_t> HWComposer::getActiveMode( + PhysicalDisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX)); const auto hwcId = *fromPhysicalDisplayId(displayId); hal::HWConfigId configId; const auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId)); + if (error == hal::Error::BAD_CONFIG) { + return ftl::Unexpected(NO_INIT); + } - RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, std::nullopt); + RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, ftl::Unexpected(UNKNOWN_ERROR)); return configId; } @@ -353,15 +364,13 @@ ui::DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId RETURN_IF_INVALID_DISPLAY(displayId, ui::DisplayConnectionType::Internal); const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay; - ui::DisplayConnectionType type; - const auto error = hwcDisplay->getConnectionType(&type); - - const auto FALLBACK_TYPE = hwcDisplay->getId() == mPrimaryHwcDisplayId - ? ui::DisplayConnectionType::Internal - : ui::DisplayConnectionType::External; - - RETURN_IF_HWC_ERROR(error, displayId, FALLBACK_TYPE); - return type; + if (const auto connectionType = hwcDisplay->getConnectionType()) { + return connectionType.value(); + } else { + LOG_HWC_ERROR(__func__, connectionType.error(), displayId); + return hwcDisplay->getId() == mPrimaryHwcDisplayId ? ui::DisplayConnectionType::Internal + : ui::DisplayConnectionType::External; + } } bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const { @@ -369,20 +378,20 @@ bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported(); } -status_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const { - RETURN_IF_INVALID_DISPLAY(displayId, 0); +ftl::Expected<nsecs_t, status_t> HWComposer::getDisplayVsyncPeriod( + PhysicalDisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX)); if (!isVsyncPeriodSwitchSupported(displayId)) { - return INVALID_OPERATION; + return ftl::Unexpected(INVALID_OPERATION); } + const auto hwcId = *fromPhysicalDisplayId(displayId); Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0; - auto error = + const auto error = static_cast<hal::Error>(mComposer->getDisplayVsyncPeriod(hwcId, &vsyncPeriodNanos)); - RETURN_IF_HWC_ERROR(error, displayId, 0); - *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos); - return NO_ERROR; + RETURN_IF_HWC_ERROR(error, displayId, ftl::Unexpected(UNKNOWN_ERROR)); + return static_cast<nsecs_t>(vsyncPeriodNanos); } std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 4ca528a6e7..7fbbb1a672 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -25,6 +25,7 @@ #include <vector> #include <android-base/thread_annotations.h> +#include <ftl/expected.h> #include <ftl/future.h> #include <ui/DisplayIdentification.h> #include <ui/FenceTime.h> @@ -237,7 +238,7 @@ public: virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId, int32_t maxFrameIntervalNs) const = 0; - virtual std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const = 0; + virtual ftl::Expected<hal::HWConfigId, status_t> getActiveMode(PhysicalDisplayId) const = 0; virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0; @@ -247,8 +248,7 @@ public: // Composer 2.4 virtual ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0; virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0; - virtual status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const = 0; + virtual ftl::Expected<nsecs_t, status_t> getDisplayVsyncPeriod(PhysicalDisplayId) const = 0; virtual status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline* outTimeline) = 0; @@ -424,7 +424,7 @@ public: std::vector<HWCDisplayMode> getModes(PhysicalDisplayId, int32_t maxFrameIntervalNs) const override; - std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const override; + ftl::Expected<hal::HWConfigId, status_t> getActiveMode(PhysicalDisplayId) const override; std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override; @@ -435,8 +435,7 @@ public: // Composer 2.4 ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override; - status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const override; + ftl::Expected<nsecs_t, status_t> getDisplayVsyncPeriod(PhysicalDisplayId) const override; status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline* outTimeline) override; @@ -491,6 +490,7 @@ public: private: // For unit tests friend TestableSurfaceFlinger; + friend HWComposerTest; struct DisplayData { std::unique_ptr<HWC2::Display> hwcDisplay; @@ -542,6 +542,7 @@ private: const size_t mMaxVirtualDisplayDimension; const bool mUpdateDeviceProductInfoOnHotplugReconnect; + bool mEnableVrrTimeout; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h index 31c2833229..e3d962237b 100644 --- a/services/surfaceflinger/DisplayHardware/Hal.h +++ b/services/surfaceflinger/DisplayHardware/Hal.h @@ -169,10 +169,8 @@ inline std::string to_string( out << "}, "; out << "notifyExpectedPresentConfig={"; if (vrrConfig->notifyExpectedPresentConfig) { - out << "notifyExpectedPresentHeadsUpNs=" - << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentHeadsUpNs - << ", notifyExpectedPresentTimeoutNs=" - << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs; + out << "headsUpNs=" << vrrConfig->notifyExpectedPresentConfig->headsUpNs + << ", timeoutNs=" << vrrConfig->notifyExpectedPresentConfig->timeoutNs; } out << "}}"; return out.str(); diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index c4ff9cc6be..12ab2c284a 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -291,6 +291,7 @@ std::vector<Capability> HidlComposer::getCapabilities() { std::string HidlComposer::dumpDebugInfo() { std::string info; + info += std::string(mComposer->descriptor) + "\n"; mComposer->dumpDebugInfo([&](const auto& tmpInfo) { info = tmpInfo.c_str(); }); return info; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index dd228b4523..a0c943ba72 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -31,10 +31,6 @@ #include <utils/Mutex.h> #include <utils/Trace.h> -#include <aidl/android/hardware/power/IPower.h> -#include <aidl/android/hardware/power/IPowerHintSession.h> -#include <aidl/android/hardware/power/WorkDuration.h> - #include <binder/IServiceManager.h> #include "../SurfaceFlingerProperties.h" diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 0276e44986..bbe51cc09d 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -25,9 +25,14 @@ #include <ui/FenceTime.h> #include <utils/Mutex.h> +// FMQ library in IPower does questionable conversions +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" #include <aidl/android/hardware/power/IPower.h> -#include <compositionengine/impl/OutputCompositionState.h> #include <powermanager/PowerHalController.h> +#pragma clang diagnostic pop + +#include <compositionengine/impl/OutputCompositionState.h> #include <scheduler/Time.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp index 155cf4da58..a47348fbc4 100644 --- a/services/surfaceflinger/FpsReporter.cpp +++ b/services/surfaceflinger/FpsReporter.cpp @@ -26,13 +26,12 @@ namespace android { -FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger, - std::unique_ptr<Clock> clock) - : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) { +FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, std::unique_ptr<Clock> clock) + : mFrameTimeline(frameTimeline), mClock(std::move(clock)) { LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!"); } -void FpsReporter::dispatchLayerFps() { +void FpsReporter::dispatchLayerFps(const frontend::LayerHierarchy& layerHierarchy) { const auto now = mClock->now(); if (now - mLastDispatch < kMinDispatchDuration) { return; @@ -52,31 +51,42 @@ void FpsReporter::dispatchLayerFps() { } std::unordered_set<int32_t> seenTasks; - std::vector<std::pair<TrackedListener, sp<Layer>>> listenersAndLayersToReport; + std::vector<std::pair<TrackedListener, const frontend::LayerHierarchy*>> + listenersAndLayersToReport; - mFlinger.mCurrentState.traverse([&](Layer* layer) { - auto& currentState = layer->getDrawingState(); - if (currentState.metadata.has(gui::METADATA_TASK_ID)) { - int32_t taskId = currentState.metadata.getInt32(gui::METADATA_TASK_ID, 0); + layerHierarchy.traverse([&](const frontend::LayerHierarchy& hierarchy, + const frontend::LayerHierarchy::TraversalPath& traversalPath) { + if (traversalPath.variant == frontend::LayerHierarchy::Variant::Detached) { + return false; + } + const auto& metadata = hierarchy.getLayer()->metadata; + if (metadata.has(gui::METADATA_TASK_ID)) { + int32_t taskId = metadata.getInt32(gui::METADATA_TASK_ID, 0); if (seenTasks.count(taskId) == 0) { // localListeners is expected to be tiny for (TrackedListener& listener : localListeners) { if (listener.taskId == taskId) { seenTasks.insert(taskId); - listenersAndLayersToReport.push_back( - {listener, sp<Layer>::fromExisting(layer)}); + listenersAndLayersToReport.push_back({listener, &hierarchy}); break; } } } } + return true; }); - for (const auto& [listener, layer] : listenersAndLayersToReport) { + for (const auto& [listener, hierarchy] : listenersAndLayersToReport) { std::unordered_set<int32_t> layerIds; - layer->traverse(LayerVector::StateSet::Current, - [&](Layer* layer) { layerIds.insert(layer->getSequence()); }); + hierarchy->traverse([&](const frontend::LayerHierarchy& hierarchy, + const frontend::LayerHierarchy::TraversalPath& traversalPath) { + if (traversalPath.variant == frontend::LayerHierarchy::Variant::Detached) { + return false; + } + layerIds.insert(static_cast<int32_t>(hierarchy.getLayer()->id)); + return true; + }); listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds)); } diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h index 438b1aa362..01f1e07b26 100644 --- a/services/surfaceflinger/FpsReporter.h +++ b/services/surfaceflinger/FpsReporter.h @@ -24,6 +24,7 @@ #include "Clock.h" #include "FrameTimeline/FrameTimeline.h" +#include "FrontEnd/LayerHierarchy.h" #include "WpHash.h" namespace android { @@ -33,13 +34,13 @@ class SurfaceFlinger; class FpsReporter : public IBinder::DeathRecipient { public: - FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger, + FpsReporter(frametimeline::FrameTimeline& frameTimeline, std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>()); // Dispatches updated layer fps values for the registered listeners // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock // must be held when calling this method. - void dispatchLayerFps() EXCLUDES(mMutex); + void dispatchLayerFps(const frontend::LayerHierarchy&) EXCLUDES(mMutex); // Override for IBinder::DeathRecipient void binderDied(const wp<IBinder>&) override; @@ -58,7 +59,6 @@ private: }; frametimeline::FrameTimeline& mFrameTimeline; - SurfaceFlinger& mFlinger; static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration = std::chrono::milliseconds(500); std::unique_ptr<Clock> mClock; diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp index 29c9432af6..8e28cc3c17 100644 --- a/services/surfaceflinger/FrameTimeline/Android.bp +++ b/services/surfaceflinger/FrameTimeline/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library_static { diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 803299cf6f..d0e2d7a451 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -390,6 +390,22 @@ void SurfaceFrame::setGpuComposition() { mGpuComposition = true; } +// TODO(b/316171339): migrate from perfetto side +bool SurfaceFrame::isSelfJanky() const { + int32_t jankType = getJankType().value_or(JankType::None); + + if (jankType == JankType::None) { + return false; + } + + int32_t jankBitmask = JankType::AppDeadlineMissed | JankType::Unknown; + if (jankType & jankBitmask) { + return true; + } + + return false; +} + std::optional<int32_t> SurfaceFrame::getJankType() const { std::scoped_lock lock(mMutex); if (mPresentState == PresentState::Dropped) { @@ -1113,20 +1129,23 @@ void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, } void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousActualPresentTime) const { + nsecs_t previousPredictionPresentTime) const { nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0; const constexpr float kThresh = 0.5f; const constexpr float kRange = 1.5f; for (auto& surfaceFrame : mSurfaceFrames) { - if (previousActualPresentTime != 0 && - static_cast<float>(mSurfaceFlingerActuals.presentTime - previousActualPresentTime) >= + if (previousPredictionPresentTime != 0 && + static_cast<float>(mSurfaceFlingerPredictions.presentTime - + previousPredictionPresentTime) >= static_cast<float>(mRenderRate.getPeriodNsecs()) * kRange && static_cast<float>(surfaceFrame->getPredictions().presentTime) <= - (static_cast<float>(mSurfaceFlingerActuals.presentTime) - + (static_cast<float>(mSurfaceFlingerPredictions.presentTime) - kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) && static_cast<float>(surfaceFrame->getPredictions().presentTime) >= - (static_cast<float>(previousActualPresentTime) - - kThresh * static_cast<float>(mRenderRate.getPeriodNsecs()))) { + (static_cast<float>(previousPredictionPresentTime) - + kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) && + // sf skipped frame is not considered if app is self janked + !surfaceFrame->isSelfJanky()) { skippedFrameStartTime = surfaceFrame->getPredictions().endTime; skippedFramePresentTime = surfaceFrame->getPredictions().presentTime; break; @@ -1215,18 +1234,18 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, }); } -void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousActualPresentTime) const { +nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + nsecs_t previousPredictionPresentTime) const { if (mSurfaceFrames.empty()) { // We don't want to trace display frames without any surface frames updates as this cannot // be janky - return; + return previousPredictionPresentTime; } if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) { // DisplayFrame should not have an invalid token. ALOGE("Cannot trace DisplayFrame with invalid token"); - return; + return previousPredictionPresentTime; } if (mPredictionState == PredictionState::Valid) { @@ -1241,8 +1260,9 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBoo } if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) { - addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousActualPresentTime); + addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime); } + return mSurfaceFlingerPredictions.presentTime; } float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) { @@ -1333,8 +1353,9 @@ void FrameTimeline::flushPendingPresentFences() { const auto& pendingPresentFence = *mPendingPresentFences.begin(); const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; auto& displayFrame = pendingPresentFence.second; - displayFrame->onPresent(signalTime, mPreviousPresentTime); - displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime); + displayFrame->onPresent(signalTime, mPreviousActualPresentTime); + mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, + mPreviousPredictionPresentTime); mPendingPresentFences.erase(mPendingPresentFences.begin()); } @@ -1349,9 +1370,10 @@ void FrameTimeline::flushPendingPresentFences() { } auto& displayFrame = pendingPresentFence.second; - displayFrame->onPresent(signalTime, mPreviousPresentTime); - displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime); - mPreviousPresentTime = signalTime; + displayFrame->onPresent(signalTime, mPreviousActualPresentTime); + mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, + mPreviousPredictionPresentTime); + mPreviousActualPresentTime = signalTime; mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); --i; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index b5047a3467..a76f7d477a 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -165,6 +165,8 @@ public: TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode); ~SurfaceFrame() = default; + bool isSelfJanky() const; + // Returns std::nullopt if the frame hasn't been classified yet. // Used by both SF and FrameTimeline. std::optional<int32_t> getJankType() const; @@ -381,8 +383,8 @@ public: // Emits a packet for perfetto tracing. The function body will be executed only if tracing // is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME // and SYSTEM_TIME_MONOTONIC. - void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousActualPresentTime) const; + nsecs_t trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + nsecs_t previousPredictionPresentTime) const; // Sets the token, vsyncPeriod, predictions and SF start time. void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate, std::optional<TimelineItem> predictions, nsecs_t wakeUpTime); @@ -508,7 +510,8 @@ private: uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; const pid_t mSurfaceFlingerPid; - nsecs_t mPreviousPresentTime = 0; + nsecs_t mPreviousActualPresentTime = 0; + nsecs_t mPreviousPredictionPresentTime = 0; const JankClassificationThresholds mJankClassificationThresholds; static constexpr uint32_t kDefaultMaxDisplayFrames = 64; // The initial container size for the vector<SurfaceFrames> inside display frame. Although diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index 1e5a6fbd1e..821ac0cf88 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -190,8 +190,12 @@ bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const { return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID; } -LayerHierarchyBuilder::LayerHierarchyBuilder( - const std::vector<std::unique_ptr<RequestedLayerState>>& layers) { +void LayerHierarchyBuilder::init(const std::vector<std::unique_ptr<RequestedLayerState>>& layers) { + mLayerIdToHierarchy.clear(); + mHierarchies.clear(); + mRoot = nullptr; + mOffscreenRoot = nullptr; + mHierarchies.reserve(layers.size()); mLayerIdToHierarchy.reserve(layers.size()); for (auto& layer : layers) { @@ -202,6 +206,7 @@ LayerHierarchyBuilder::LayerHierarchyBuilder( onLayerAdded(layer.get()); } detachHierarchyFromRelativeParent(&mOffscreenRoot); + mInitialized = true; } void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) { @@ -332,7 +337,7 @@ void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) { } } -void LayerHierarchyBuilder::update( +void LayerHierarchyBuilder::doUpdate( const std::vector<std::unique_ptr<RequestedLayerState>>& layers, const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) { // rebuild map @@ -381,6 +386,32 @@ void LayerHierarchyBuilder::update( attachHierarchyToRelativeParent(&mRoot); } +void LayerHierarchyBuilder::update(LayerLifecycleManager& layerLifecycleManager) { + if (!mInitialized) { + ATRACE_NAME("LayerHierarchyBuilder:init"); + init(layerLifecycleManager.getLayers()); + } else if (layerLifecycleManager.getGlobalChanges().test( + RequestedLayerState::Changes::Hierarchy)) { + ATRACE_NAME("LayerHierarchyBuilder:update"); + doUpdate(layerLifecycleManager.getLayers(), layerLifecycleManager.getDestroyedLayers()); + } else { + return; // nothing to do + } + + uint32_t invalidRelativeRoot; + bool hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot); + while (hasRelZLoop) { + ATRACE_NAME("FixRelZLoop"); + TransactionTraceWriter::getInstance().invoke("relz_loop_detected", + /*overwrite=*/false); + layerLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); + // reinitialize the hierarchy with the updated layer data + init(layerLifecycleManager.getLayers()); + // check if we have any remaining loops + hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot); + } +} + const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const { return mRoot; } diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index ba2e262baf..a1c73c38b0 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -17,6 +17,7 @@ #pragma once #include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerLifecycleManager.h" #include "RequestedLayerState.h" #include "ftl/small_vector.h" @@ -197,9 +198,8 @@ private: // hierarchy from a list of RequestedLayerState and associated change flags. class LayerHierarchyBuilder { public: - LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&); - void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers, - const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers); + LayerHierarchyBuilder() = default; + void update(LayerLifecycleManager& layerLifecycleManager); LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const; const LayerHierarchy& getHierarchy() const; const LayerHierarchy& getOffscreenHierarchy() const; @@ -213,14 +213,18 @@ private: void detachFromRelativeParent(LayerHierarchy*); void attachHierarchyToRelativeParent(LayerHierarchy*); void detachHierarchyFromRelativeParent(LayerHierarchy*); - + void init(const std::vector<std::unique_ptr<RequestedLayerState>>&); + void doUpdate(const std::vector<std::unique_ptr<RequestedLayerState>>& layers, + const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers); void onLayerDestroyed(RequestedLayerState* layer); void updateMirrorLayer(RequestedLayerState* layer); LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true); + std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy; std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies; LayerHierarchy mRoot{nullptr}; LayerHierarchy mOffscreenRoot{nullptr}; + bool mInitialized = false; }; } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 38974a2f58..ea06cf6de6 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -315,6 +315,7 @@ std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) { if (obj.hasInputInfo()) { out << "\n input{" << "(" << obj.inputInfo.inputConfig.string() << ")"; + if (obj.inputInfo.canOccludePresentation) out << " canOccludePresentation"; if (obj.touchCropId != UNASSIGNED_LAYER_ID) out << " touchCropId=" << obj.touchCropId; if (obj.inputInfo.replaceTouchableRegionWithCrop) out << " replaceTouchableRegionWithCrop"; auto touchableRegion = obj.inputInfo.touchableRegion.getBounds(); @@ -380,6 +381,9 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate currentHdrSdrRatio = requested.currentHdrSdrRatio; desiredHdrSdrRatio = requested.desiredHdrSdrRatio; } + if (forceUpdate || requested.what & layer_state_t::eDesiredHdrHeadroomChanged) { + desiredHdrSdrRatio = requested.desiredHdrSdrRatio; + } if (forceUpdate || requested.what & layer_state_t::eCachingHintChanged) { cachingHint = requested.cachingHint; } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index ad5e42be37..0966fe0496 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -587,8 +587,8 @@ LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::Traver bool LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) { if (!mResortSnapshots && args.forceUpdate == ForceUpdateFlags::NONE && !args.layerLifecycleManager.getGlobalChanges().any( - RequestedLayerState::Changes::Hierarchy | - RequestedLayerState::Changes::Visibility)) { + RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility | + RequestedLayerState::Changes::Input)) { // We are not force updating and there are no hierarchy or visibility changes. Avoid sorting // the snapshots. return false; @@ -1044,6 +1044,8 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo() ? requested.windowInfoHandle->getInfo()->touchOcclusionMode : parentSnapshot.inputInfo.touchOcclusionMode; + snapshot.inputInfo.canOccludePresentation = parentSnapshot.inputInfo.canOccludePresentation || + (requested.flags & layer_state_t::eCanOccludePresentation); if (requested.dropInputMode == gui::DropInputMode::ALL || parentSnapshot.dropInputMode == gui::DropInputMode::ALL) { snapshot.dropInputMode = gui::DropInputMode::ALL; @@ -1217,8 +1219,8 @@ void LayerSnapshotBuilder::updateTouchableRegionCrop(const Args& args) { Rect inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); - snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect( - displayInfo.transform.transform(inputBoundsInDisplaySpace)); + snapshot->inputInfo.touchableRegion = + snapshot->inputInfo.touchableRegion.intersect(inputBoundsInDisplaySpace); } // If the layer is a clone, we need to crop the input region to cloned root to prevent diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 209df79f8e..cb0e2a1938 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -98,7 +98,7 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) z = 0; layerStack = ui::DEFAULT_LAYER_STACK; transformToDisplayInverse = false; - desiredHdrSdrRatio = 1.f; + desiredHdrSdrRatio = -1.f; currentHdrSdrRatio = 1.f; dataspaceRequested = false; hdrMetadata.validTypes = 0; @@ -170,6 +170,9 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) { changes |= RequestedLayerState::Changes::Geometry; } + if ((oldFlags ^ flags) & layer_state_t::eCanOccludePresentation) { + changes |= RequestedLayerState::Changes::Input; + } } if (clientState.what & layer_state_t::eBufferChanged) { @@ -603,7 +606,8 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { layer_state_t::eShadowRadiusChanged | layer_state_t::eFixedTransformHintChanged | layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged | layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged | - layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged; + layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged | + layer_state_t::eDesiredHdrHeadroomChanged; if (changedFlags & deniedChanges) { ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__, s.what & deniedChanges); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index c8b1059b94..736fec6fb2 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -78,11 +78,13 @@ #include "SurfaceFlinger.h" #include "TimeStats/TimeStats.h" #include "TunnelModeEnabledReporter.h" +#include "Utils/FenceUtils.h" #define DEBUG_RESIZE 0 #define EARLY_RELEASE_ENABLED false namespace android { +using namespace std::chrono_literals; namespace { constexpr int kDumpTableRowLength = 159; @@ -2911,7 +2913,8 @@ void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& l } void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, - ui::LayerStack layerStack) { + ui::LayerStack layerStack, + std::function<FenceResult(FenceResult)>&& continuation) { // If we are displayed on multiple displays in a single composition cycle then we would // need to do careful tracking to enable the use of the mLastClientCompositionFence. // For example we can only use it if all the displays are client comp, and we need @@ -2946,11 +2949,41 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, break; } } + + if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) { + futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share(); + } + if (ch != nullptr) { ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); ch->name = mName; + } else if (FlagManager::getInstance().screenshot_fence_preservation()) { + // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots + // asynchronously, then make sure we don't drop the fence. + mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult), + std::move(continuation)); + std::vector<FenceAndContinuation> mergedFences; + sp<Fence> prevFence = nullptr; + // For a layer that's frequently screenshotted, try to merge fences to make sure we don't + // grow unbounded. + for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) { + auto result = futureAndContinution.future.wait_for(0s); + if (result != std::future_status::ready) { + mergedFences.emplace_back(futureAndContinution); + continue; + } + + mergeFence(getDebugName(), futureAndContinution.chain().get().value_or(Fence::NO_FENCE), + prevFence); + } + if (prevFence != nullptr) { + mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share()); + } + + mAdditionalPreviousReleaseFences.swap(mergedFences); } + if (mBufferInfo.mBuffer) { mPreviouslyPresentedLayerStacks.push_back(layerStack); } @@ -3362,6 +3395,14 @@ bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRa return true; } +bool Layer::setDesiredHdrHeadroom(float desiredRatio) { + if (mDrawingState.desiredHdrSdrRatio == desiredRatio) return false; + mDrawingState.desiredHdrSdrRatio = desiredRatio; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + bool Layer::setCachingHint(gui::CachingHint cachingHint) { if (mDrawingState.cachingHint == cachingHint) return false; mDrawingState.cachingHint = cachingHint; @@ -3440,6 +3481,15 @@ bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence; handle->frameNumber = mDrawingState.frameNumber; handle->previousFrameNumber = mDrawingState.previousFrameNumber; + if (FlagManager::getInstance().screenshot_fence_preservation() && + mPreviousReleaseBufferEndpoint == handle->listener) { + // Add fences from previous screenshots now so that they can be dispatched to the + // client. + for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) { + handle->previousReleaseFences.emplace_back(futureAndContinution.chain()); + } + mAdditionalPreviousReleaseFences.clear(); + } // Store so latched time and release fence can be set mDrawingState.callbackHandles.push_back(handle); @@ -3949,6 +3999,13 @@ bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const { } } + if (s.what & layer_state_t::eDesiredHdrHeadroomChanged) { + if (mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) { + ATRACE_FORMAT_INSTANT("%s: false [eDesiredHdrHeadroomChanged changed]", __func__); + return false; + } + } + return true; } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index c772e0ea1b..0ceecec7ec 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -233,7 +233,7 @@ public: bool autoRefresh = false; bool dimmingEnabled = true; float currentHdrSdrRatio = 1.f; - float desiredHdrSdrRatio = 1.f; + float desiredHdrSdrRatio = -1.f; gui::CachingHint cachingHint = gui::CachingHint::Enabled; int64_t latchedVsyncId = 0; bool useVsyncIdForRefreshRateSelection = false; @@ -317,6 +317,7 @@ public: void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/); bool setDataspace(ui::Dataspace /*dataspace*/); bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio); + bool setDesiredHdrHeadroom(float desiredRatio); bool setCachingHint(gui::CachingHint cachingHint); bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/); bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/); @@ -546,7 +547,7 @@ public: sp<IBinder> mReleaseBufferEndpoint; bool mFrameLatencyNeeded{false}; - float mDesiredHdrSdrRatio = 1.f; + float mDesiredHdrSdrRatio = -1.f; }; BufferInfo mBufferInfo; @@ -555,7 +556,8 @@ public: const compositionengine::LayerFECompositionState* getCompositionState() const; bool fenceHasSignaled() const; void onPreComposition(nsecs_t refreshStartTime); - void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack); + void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack, + std::function<FenceResult(FenceResult)>&& continuation = nullptr); void setWasClientComposed(const sp<Fence>& fence) { mLastClientCompositionFence = fence; @@ -932,6 +934,19 @@ public: // the release fences from the correct displays when we release the last buffer // from the layer. std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks; + struct FenceAndContinuation { + ftl::SharedFuture<FenceResult> future; + std::function<FenceResult(FenceResult)> continuation; + + ftl::SharedFuture<FenceResult> chain() const { + if (continuation) { + return ftl::Future(future).then(continuation).share(); + } else { + return future; + } + } + }; + std::vector<FenceAndContinuation> mAdditionalPreviousReleaseFences; // Exposed so SurfaceFlinger can assert that it's held const sp<SurfaceFlinger> mFlinger; diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp index d71484889d..16776cf18a 100644 --- a/services/surfaceflinger/Scheduler/Android.bp +++ b/services/surfaceflinger/Scheduler/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 693a357de5..96eccf290f 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -100,6 +100,11 @@ std::string toString(const DisplayEventReceiver::Event& event) { case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: return StringPrintf("ModeChanged{displayId=%s, modeId=%u}", to_string(event.header.displayId).c_str(), event.modeChange.modeId); + case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: + return StringPrintf("HdcpLevelsChange{displayId=%s, connectedLevel=%d, maxLevel=%d}", + to_string(event.header.displayId).c_str(), + event.hdcpLevelsChange.connectedLevel, + event.hdcpLevelsChange.maxLevel); default: return "Event{}"; } @@ -143,7 +148,7 @@ DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode DisplayEventReceiver::Event event; event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode.modePtr->getPhysicalDisplayId(), systemTime()}; - event.modeChange.modeId = mode.modePtr->getId().value(); + event.modeChange.modeId = ftl::to_underlying(mode.modePtr->getId()); event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs(); return event; } @@ -170,6 +175,20 @@ DisplayEventReceiver::Event makeFrameRateOverrideFlushEvent(PhysicalDisplayId di }}; } +DisplayEventReceiver::Event makeHdcpLevelsChange(PhysicalDisplayId displayId, + int32_t connectedLevel, int32_t maxLevel) { + return DisplayEventReceiver::Event{ + .header = + DisplayEventReceiver::Event::Header{ + .type = DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, + .displayId = displayId, + .timestamp = systemTime(), + }, + .hdcpLevelsChange.connectedLevel = connectedLevel, + .hdcpLevelsChange.maxLevel = maxLevel, + }; +} + } // namespace EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid, @@ -301,7 +320,7 @@ void EventThread::setDuration(std::chrono::nanoseconds workDuration, mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .earliestVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns()}); } sp<EventThreadConnection> EventThread::createEventConnection( @@ -385,6 +404,9 @@ VsyncEventData EventThread::getLatestVsyncEventData( }(); generateFrameTimeline(vsyncEventData, frameInterval.ns(), systemTime(SYSTEM_TIME_MONOTONIC), presentTime, deadline); + if (FlagManager::getInstance().vrr_config()) { + mCallback.onExpectedPresentTimePosted(TimePoint::fromNs(presentTime)); + } return vsyncEventData; } @@ -442,6 +464,14 @@ void EventThread::onFrameRateOverridesChanged(PhysicalDisplayId displayId, mCondition.notify_all(); } +void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) { + std::lock_guard<std::mutex> lock(mMutex); + + mPendingEvents.push_back(makeHdcpLevelsChange(displayId, connectedLevel, maxLevel)); + mCondition.notify_all(); +} + void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { DisplayEventConsumers consumers; @@ -501,7 +531,7 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { const auto scheduleResult = mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .earliestVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns()}); LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback"); } else { mVsyncRegistration.cancel(); @@ -557,6 +587,9 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: return true; + case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: + return true; + case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: { return connection->mEventRegistration.test( gui::ISurfaceComposer::EventRegistration::modeChanged); @@ -691,6 +724,11 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, removeDisplayEventConnectionLocked(consumer); } } + if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC && + FlagManager::getInstance().vrr_config()) { + mCallback.onExpectedPresentTimePosted( + TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime())); + } } void EventThread::dump(std::string& result) const { @@ -757,7 +795,7 @@ scheduler::VSyncCallbackRegistration EventThread::onNewVsyncScheduleInternal( if (reschedule) { mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .earliestVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns()}); } return oldRegistration; } diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 7842318e2e..90e61a984b 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -131,6 +131,9 @@ public: const sp<EventThreadConnection>& connection) const = 0; virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0; + + virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) = 0; }; struct IEventThreadCallback { @@ -139,6 +142,7 @@ struct IEventThreadCallback { virtual bool throttleVsync(TimePoint, uid_t) = 0; virtual Period getVsyncPeriod(uid_t) = 0; virtual void resync() = 0; + virtual void onExpectedPresentTimePosted(TimePoint) = 0; }; namespace impl { @@ -177,6 +181,9 @@ public: void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex); + void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) override; + private: friend EventThreadTest; diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h index 3b61de7e83..9f4f5b6d39 100644 --- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h +++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h @@ -30,6 +30,8 @@ struct ISchedulerCallback { virtual void kernelTimerChanged(bool expired) = 0; virtual void triggerOnFrameRateOverridesChanged() = 0; virtual void onChoreographerAttached() = 0; + virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, + Fps renderRate) = 0; protected: ~ISchedulerCallback() = default; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 5ce883ce39..b8d5e76358 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -221,9 +221,8 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - const std::string categoryString = vote.category == FrameRateCategory::Default ? "" : base::StringPrintf("category=%s", ftl::enum_string(vote.category).c_str()); - ATRACE_FORMAT_INSTANT("%s %s %s (%d%)", ftl::enum_string(vote.type).c_str(), - to_string(vote.fps).c_str(), categoryString.c_str(), - weight * 100); + ATRACE_FORMAT_INSTANT("%s %s %s (%.2f)", ftl::enum_string(vote.type).c_str(), + to_string(vote.fps).c_str(), categoryString.c_str(), weight); summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps, vote.seamlessness, vote.category, vote.categorySmoothSwitchOnly, weight, layerFocused}); @@ -301,12 +300,26 @@ void LayerHistory::partitionLayers(nsecs_t now) { if (gameModeFrameRateOverride.isValid()) { info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride}); + ATRACE_FORMAT_INSTANT("GameModeFrameRateOverride"); + if (CC_UNLIKELY(mTraceEnabled)) { + trace(*info, gameFrameRateOverrideVoteType, + gameModeFrameRateOverride.getIntValue()); + } } else if (frameRate.isValid()) { info->setLayerVote({setFrameRateVoteType, frameRate.vote.rate, frameRate.vote.seamlessness, frameRate.category}); + if (CC_UNLIKELY(mTraceEnabled)) { + trace(*info, gameFrameRateOverrideVoteType, + frameRate.vote.rate.getIntValue()); + } } else if (gameDefaultFrameRateOverride.isValid()) { info->setLayerVote( {gameFrameRateOverrideVoteType, gameDefaultFrameRateOverride}); + ATRACE_FORMAT_INSTANT("GameDefaultFrameRateOverride"); + if (CC_UNLIKELY(mTraceEnabled)) { + trace(*info, gameFrameRateOverrideVoteType, + gameDefaultFrameRateOverride.getIntValue()); + } } else { info->resetLayerVote(); } @@ -342,9 +355,18 @@ void LayerHistory::clear() { std::string LayerHistory::dump() const { std::lock_guard lock(mLock); - return base::StringPrintf("{size=%zu, active=%zu}", + return base::StringPrintf("{size=%zu, active=%zu}\n\tGameFrameRateOverrides=\n\t\t%s", mActiveLayerInfos.size() + mInactiveLayerInfos.size(), - mActiveLayerInfos.size()); + mActiveLayerInfos.size(), dumpGameFrameRateOverridesLocked().c_str()); +} + +std::string LayerHistory::dumpGameFrameRateOverridesLocked() const { + std::string overridesString = "(uid, gameModeOverride, gameDefaultOverride)="; + for (auto it = mGameFrameRateOverride.begin(); it != mGameFrameRateOverride.end(); ++it) { + base::StringAppendF(&overridesString, "{%u, %d %d} ", it->first, + it->second.first.getIntValue(), it->second.second.getIntValue()); + } + return overridesString; } float LayerHistory::getLayerFramerate(nsecs_t now, int32_t id) const { diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index 930d06c8f0..a6f1b56bf2 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -108,6 +108,8 @@ private: // keyed by id as returned from Layer::getSequence() using LayerInfos = std::unordered_map<int32_t, LayerPair>; + std::string dumpGameFrameRateOverridesLocked() const REQUIRES(mLock); + // Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive // layers to mInactiveLayerInfos. // worst case time complexity is O(2 * inactive + active) diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 97fca395b4..9745452e89 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -62,6 +62,10 @@ void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUp mLastAnimationTime = std::max(lastPresentTime, now); break; case LayerUpdateType::SetFrameRate: + if (FlagManager::getInstance().vrr_config()) { + break; + } + FALLTHROUGH_INTENDED; case LayerUpdateType::Buffer: FrameTimeData frameTime = {.presentTime = lastPresentTime, .queueTime = mLastUpdatedTime, @@ -323,7 +327,8 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) { ATRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str()); ALOGV("%s voted %d", mName.c_str(), static_cast<int>(mLayerVote.type)); - votes.push_back(mLayerVote); + votes.push_back({mLayerVote.type, mLayerVote.fps, mLayerVote.seamlessness, + FrameRateCategory::Default, mLayerVote.categorySmoothSwitchOnly}); } return votes; @@ -528,6 +533,8 @@ FrameRateCategory LayerInfo::FrameRate::convertCategory(int8_t category) { return FrameRateCategory::Low; case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL: return FrameRateCategory::Normal; + case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH_HINT: + return FrameRateCategory::HighHint; case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH: return FrameRateCategory::High; default: diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index 18c0a696d5..ff88d71259 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -17,7 +17,6 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <binder/IPCThreadState.h> -#include <gui/DisplayEventReceiver.h> #include <utils/Log.h> #include <utils/Timers.h> #include <utils/threads.h> @@ -66,7 +65,7 @@ void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, ns { std::lock_guard lock(mVsync.mutex); mVsync.lastCallbackTime = expectedVsyncTime; - mVsync.scheduledFrameTime.reset(); + mVsync.scheduledFrameTimeOpt.reset(); } const auto vsyncId = VsyncId{mVsync.tokenManager->generateTokenForPredictions( @@ -75,9 +74,9 @@ void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, ns mHandler->dispatchFrame(vsyncId, expectedVsyncTime); } -void MessageQueue::initVsync(std::shared_ptr<scheduler::VSyncDispatch> dispatch, - frametimeline::TokenManager& tokenManager, - std::chrono::nanoseconds workDuration) { +void MessageQueue::initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch> dispatch, + frametimeline::TokenManager& tokenManager, + std::chrono::nanoseconds workDuration) { std::unique_ptr<scheduler::VSyncCallbackRegistration> oldRegistration; { std::lock_guard lock(mVsync.mutex); @@ -87,7 +86,7 @@ void MessageQueue::initVsync(std::shared_ptr<scheduler::VSyncDispatch> dispatch, } // See comments in onNewVsyncSchedule. Today, oldRegistration should be - // empty, but nothing prevents us from calling initVsync multiple times, so + // empty, but nothing prevents us from calling initVsyncInternal multiple times, so // go ahead and destruct it outside the lock for safety. oldRegistration.reset(); } @@ -122,10 +121,10 @@ std::unique_ptr<scheduler::VSyncCallbackRegistration> MessageQueue::onNewVsyncSc std::placeholders::_3), "sf"); if (reschedule) { - mVsync.scheduledFrameTime = + mVsync.scheduledFrameTimeOpt = mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.ns()}); + .lastVsync = mVsync.lastCallbackTime.ns()}); } return oldRegistration; } @@ -140,10 +139,10 @@ void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) { ATRACE_CALL(); std::lock_guard lock(mVsync.mutex); mVsync.workDuration = workDuration; - mVsync.scheduledFrameTime = + mVsync.scheduledFrameTimeOpt = mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(), .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.ns()}); + .lastVsync = mVsync.lastCallbackTime.ns()}); } void MessageQueue::waitMessage() { @@ -193,22 +192,20 @@ void MessageQueue::scheduleFrame() { ATRACE_CALL(); std::lock_guard lock(mVsync.mutex); - mVsync.scheduledFrameTime = + mVsync.scheduledFrameTimeOpt = mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.ns()}); + .lastVsync = mVsync.lastCallbackTime.ns()}); } -auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> { +std::optional<scheduler::ScheduleResult> MessageQueue::getScheduledFrameResult() const { if (mHandler->isFramePending()) { - return Clock::now(); + return scheduler::ScheduleResult{TimePoint::now(), mHandler->getExpectedVsyncTime()}; } - std::lock_guard lock(mVsync.mutex); - if (const auto time = mVsync.scheduledFrameTime) { - return Clock::time_point(std::chrono::nanoseconds(*time)); + if (const auto scheduledFrameTimeline = mVsync.scheduledFrameTimeOpt) { + return scheduledFrameTimeline; } - return std::nullopt; } diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index a523147733..c5fc371d3a 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -65,8 +65,9 @@ class MessageQueue { public: virtual ~MessageQueue() = default; - virtual void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&, - std::chrono::nanoseconds workDuration) = 0; + virtual void initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch>, + frametimeline::TokenManager&, + std::chrono::nanoseconds workDuration) = 0; virtual void destroyVsync() = 0; virtual void setDuration(std::chrono::nanoseconds workDuration) = 0; virtual void waitMessage() = 0; @@ -75,8 +76,7 @@ public: virtual void scheduleConfigure() = 0; virtual void scheduleFrame() = 0; - using Clock = std::chrono::steady_clock; - virtual std::optional<Clock::time_point> getScheduledFrameTime() const = 0; + virtual std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const = 0; }; namespace impl { @@ -94,7 +94,9 @@ protected: explicit Handler(MessageQueue& queue) : mQueue(queue) {} void handleMessage(const Message& message) override; - bool isFramePending() const; + virtual TimePoint getExpectedVsyncTime() const { return mExpectedVsyncTime.load(); } + + virtual bool isFramePending() const; virtual void dispatchFrame(VsyncId, TimePoint expectedVsyncTime); }; @@ -123,7 +125,7 @@ private: TracedOrdinal<std::chrono::nanoseconds> workDuration GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)}; TimePoint lastCallbackTime GUARDED_BY(mutex); - std::optional<nsecs_t> scheduledFrameTime GUARDED_BY(mutex); + std::optional<scheduler::ScheduleResult> scheduledFrameTimeOpt GUARDED_BY(mutex); TracedOrdinal<int> value = {"VSYNC-sf", 0}; }; @@ -137,8 +139,8 @@ private: public: explicit MessageQueue(ICompositor&); - void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&, - std::chrono::nanoseconds workDuration) override; + void initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&, + std::chrono::nanoseconds workDuration) override; void destroyVsync() override; void setDuration(std::chrono::nanoseconds workDuration) override; @@ -149,7 +151,7 @@ public: void scheduleConfigure() override; void scheduleFrame() override; - std::optional<Clock::time_point> getScheduledFrameTime() const override; + std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const override; }; } // namespace impl diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index e06221a43d..ffd3463296 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -286,7 +286,8 @@ struct RefreshRateSelector::RefreshRateScoreComparator { std::string RefreshRateSelector::Policy::toString() const { return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s" ", primaryRanges=%s, appRequestRanges=%s}", - defaultMode.value(), allowGroupSwitching ? "true" : "false", + ftl::to_underlying(defaultMode), + allowGroupSwitching ? "true" : "false", to_string(primaryRanges).c_str(), to_string(appRequestRanges).c_str()); } @@ -420,6 +421,11 @@ float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& lay const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; if (layer.vote == LayerVoteType::ExplicitCategory) { + // HighHint is considered later for touch boost. + if (layer.frameRateCategory == FrameRateCategory::HighHint) { + return 0.f; + } + if (getFrameRateCategoryRange(layer.frameRateCategory).includes(refreshRate)) { return 1.f; } @@ -507,6 +513,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi int explicitExact = 0; int explicitGteLayers = 0; int explicitCategoryVoteLayers = 0; + int interactiveLayers = 0; int seamedFocusedLayers = 0; int categorySmoothSwitchOnlyLayers = 0; @@ -534,7 +541,13 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi explicitGteLayers++; break; case LayerVoteType::ExplicitCategory: - explicitCategoryVoteLayers++; + if (layer.frameRateCategory == FrameRateCategory::HighHint) { + // HighHint does not count as an explicit signal from an app. It may be + // be a touch signal. + interactiveLayers++; + } else { + explicitCategoryVoteLayers++; + } if (layer.frameRateCategory == FrameRateCategory::NoPreference) { // Count this layer for Min vote as well. The explicit vote avoids // touch boost and idle for choosing a category, while Min vote is for correct @@ -831,8 +844,13 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; - if (signals.touch && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 && - touchBoostForExplicitExact && + // A method for UI Toolkit to send the touch signal via "HighHint" category vote, + // which will touch boost when there are no ExplicitDefault layer votes. This is an + // incomplete solution but accounts for cases such as games that use `setFrameRate` with default + // compatibility to limit the frame rate, which should not have touch boost. + const bool hasInteraction = signals.touch || interactiveLayers > 0; + + if (hasInteraction && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { ALOGV("Touch Boost"); ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])", @@ -930,17 +948,43 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme const auto layersByUid = groupLayersByUid(layers); UidToFrameRateOverride frameRateOverrides; for (const auto& [uid, layersWithSameUid] : layersByUid) { - // Layers with ExplicitExactOrMultiple expect touch boost - const bool hasExplicitExactOrMultiple = - std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(), - [](const auto& layer) { - return layer->vote == LayerVoteType::ExplicitExactOrMultiple; - }); + // Look for cases that should not have frame rate overrides. + bool hasExplicitExactOrMultiple = false; + bool hasExplicitDefault = false; + bool hasHighHint = false; + for (const auto& layer : layersWithSameUid) { + switch (layer->vote) { + case LayerVoteType::ExplicitExactOrMultiple: + hasExplicitExactOrMultiple = true; + break; + case LayerVoteType::ExplicitDefault: + hasExplicitDefault = true; + break; + case LayerVoteType::ExplicitCategory: + if (layer->frameRateCategory == FrameRateCategory::HighHint) { + hasHighHint = true; + } + break; + default: + // No action + break; + } + if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint) { + break; + } + } + // Layers with ExplicitExactOrMultiple expect touch boost if (globalSignals.touch && hasExplicitExactOrMultiple) { continue; } + // Mirrors getRankedFrameRates. If there is no ExplicitDefault, expect touch boost and + // skip frame rate override. + if (hasHighHint && !hasExplicitDefault) { + continue; + } + for (auto& [_, score] : scoredFrameRates) { score = 0; } @@ -954,6 +998,7 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault && layer->vote != LayerVoteType::ExplicitExactOrMultiple && layer->vote != LayerVoteType::ExplicitExact && + layer->vote != LayerVoteType::ExplicitGte && layer->vote != LayerVoteType::ExplicitCategory, "Invalid layer vote type for frame rate overrides"); for (auto& [fps, score] : scoredFrameRates) { @@ -1512,6 +1557,7 @@ FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory catego return FpsRange{60_Hz, 90_Hz}; case FrameRateCategory::Low: return FpsRange{30_Hz, 30_Hz}; + case FrameRateCategory::HighHint: case FrameRateCategory::NoPreference: case FrameRateCategory::Default: LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s", diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index a1a7c289cf..6051e8935d 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -31,7 +31,6 @@ #include "DisplayHardware/DisplayMode.h" #include "Scheduler/OneShotTimer.h" -#include "Scheduler/StrongTyping.h" #include "ThreadContext.h" #include "Utils/Dumper.h" diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h index 67e1b9c2ea..d51af9ac88 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -49,10 +49,10 @@ class RefreshRateStats { public: // TODO(b/185535769): Inject clock to avoid sleeping in tests. - RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate, PowerMode currentPowerMode) + RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate) : mTimeStats(timeStats), mCurrentRefreshRate(currentRefreshRate), - mCurrentPowerMode(currentPowerMode) {} + mCurrentPowerMode(PowerMode::OFF) {} void setPowerMode(PowerMode mode) { if (mCurrentPowerMode == mode) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index ce59a04808..3f9168252b 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -51,28 +51,25 @@ #include "FrameRateOverrideMappings.h" #include "FrontEnd/LayerHandle.h" #include "OneShotTimer.h" +#include "RefreshRateStats.h" +#include "SurfaceFlingerFactory.h" #include "SurfaceFlingerProperties.h" +#include "TimeStats/TimeStats.h" #include "VSyncTracker.h" +#include "VsyncConfiguration.h" #include "VsyncController.h" #include "VsyncSchedule.h" -#define RETURN_IF_INVALID_HANDLE(handle, ...) \ - do { \ - if (mConnections.count(handle) == 0) { \ - ALOGE("Invalid connection handle %" PRIuPTR, handle.id); \ - return __VA_ARGS__; \ - } \ - } while (false) - namespace android::scheduler { Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features, - sp<VsyncModulator> modulatorPtr, IVsyncTrackerCallback& vsyncTrackerCallback) - : impl::MessageQueue(compositor), + surfaceflinger::Factory& factory, Fps activeRefreshRate, TimeStats& timeStats) + : android::impl::MessageQueue(compositor), mFeatures(features), - mVsyncModulator(std::move(modulatorPtr)), - mSchedulerCallback(callback), - mVsyncTrackerCallback(vsyncTrackerCallback) {} + mVsyncConfiguration(factory.createVsyncConfiguration(activeRefreshRate)), + mVsyncModulator(sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs())), + mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate)), + mSchedulerCallback(callback) {} Scheduler::~Scheduler() { // MessageQueue depends on VsyncSchedule, so first destroy it. @@ -88,6 +85,11 @@ Scheduler::~Scheduler() { demotePacesetterDisplay(); } +void Scheduler::initVsync(frametimeline::TokenManager& tokenManager, + std::chrono::nanoseconds workDuration) { + Impl::initVsyncInternal(getVsyncSchedule()->getDispatch(), tokenManager, workDuration); +} + void Scheduler::startTimers() { using namespace sysprop; using namespace std::string_literals; @@ -122,10 +124,11 @@ void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetter } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { - auto schedulePtr = std::make_shared<VsyncSchedule>( - selectorPtr->getActiveMode().modePtr, mFeatures, - [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); }, - mVsyncTrackerCallback); + auto schedulePtr = + std::make_shared<VsyncSchedule>(selectorPtr->getActiveMode().modePtr, mFeatures, + [this](PhysicalDisplayId id, bool enable) { + onHardwareVsyncRequest(id, enable); + }); registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr)); } @@ -151,9 +154,13 @@ void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId, if (isNew) { onHardwareVsyncRequest(displayId, false); } + + dispatchHotplug(displayId, Hotplug::Connected); } void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) { + dispatchHotplug(displayId, Hotplug::Disconnected); + demotePacesetterDisplay(); std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule; @@ -182,9 +189,9 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, const FrameTargeter::BeginFrameArgs beginFrameArgs = {.frameBeginTime = SchedulerClock::now(), .vsyncId = vsyncId, - // TODO(b/255601557): Calculate per display. .expectedVsyncTime = expectedVsyncTime, - .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration}; + .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration, + .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration}; ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked(); pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr); @@ -193,15 +200,29 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, FrameTargets targets; targets.try_emplace(pacesetterPtr->displayId, &pacesetterPtr->targeterPtr->target()); + // TODO (b/256196556): Followers should use the next VSYNC after the frontrunner, not the + // pacesetter. + // Update expectedVsyncTime, which may have been adjusted by beginFrame. + expectedVsyncTime = pacesetterPtr->targeterPtr->target().expectedPresentTime(); + for (const auto& [id, display] : mDisplays) { if (id == pacesetterPtr->displayId) continue; + auto followerBeginFrameArgs = beginFrameArgs; + followerBeginFrameArgs.expectedVsyncTime = + display.schedulePtr->vsyncDeadlineAfter(expectedVsyncTime); + FrameTargeter& targeter = *display.targeterPtr; - targeter.beginFrame(beginFrameArgs, *display.schedulePtr); + targeter.beginFrame(followerBeginFrameArgs, *display.schedulePtr); targets.try_emplace(id, &targeter.target()); } - if (!compositor.commit(pacesetterPtr->displayId, targets)) return; + if (!compositor.commit(pacesetterPtr->displayId, targets)) { + if (FlagManager::getInstance().vrr_config()) { + compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId); + } + return; + } } // The pacesetter may have changed or been registered anew during commit. @@ -242,6 +263,9 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, } const auto resultsPerDisplay = compositor.composite(pacesetterPtr->displayId, targeters); + if (FlagManager::getInstance().vrr_config()) { + compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId); + } compositor.sample(); for (const auto& [id, targeter] : targeters) { @@ -302,40 +326,40 @@ Period Scheduler::getVsyncPeriod(uid_t uid) { // behaviour. return Period::fromNs(currentPeriod.ns() * divisor); } +void Scheduler::onExpectedPresentTimePosted(TimePoint expectedPresentTime) { + const auto frameRateMode = [this] { + std::scoped_lock lock(mDisplayLock); + const auto pacesetterOpt = pacesetterDisplayLocked(); + const Display& pacesetter = *pacesetterOpt; + return pacesetter.selectorPtr->getActiveMode(); + }(); -ConnectionHandle Scheduler::createEventThread(Cycle cycle, - frametimeline::TokenManager* tokenManager, - std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration) { - auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf", - getVsyncSchedule(), tokenManager, *this, - workDuration, readyDuration); - - auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle; - handle = createConnection(std::move(eventThread)); - return handle; + if (frameRateMode.modePtr->getVrrConfig()) { + mSchedulerCallback.onExpectedPresentTimePosted(expectedPresentTime, frameRateMode.modePtr, + frameRateMode.fps); + } } -ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) { - const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++}; - ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id); +void Scheduler::createEventThread(Cycle cycle, frametimeline::TokenManager* tokenManager, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) { + auto eventThread = + std::make_unique<android::impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf", + getVsyncSchedule(), tokenManager, *this, + workDuration, readyDuration); - auto connection = eventThread->createEventConnection(); - - std::lock_guard<std::mutex> lock(mConnectionsLock); - mConnections.emplace(handle, Connection{connection, std::move(eventThread)}); - return handle; + if (cycle == Cycle::Render) { + mRenderEventThread = std::move(eventThread); + mRenderEventConnection = mRenderEventThread->createEventConnection(); + } else { + mLastCompositeEventThread = std::move(eventThread); + mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection(); + } } sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( - ConnectionHandle handle, EventRegistrationFlags eventRegistration, - const sp<IBinder>& layerHandle) { - const auto connection = [&]() -> sp<EventThreadConnection> { - std::scoped_lock lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle, nullptr); - - return mConnections[handle].thread->createEventConnection(eventRegistration); - }(); + Cycle cycle, EventRegistrationFlags eventRegistration, const sp<IBinder>& layerHandle) { + const auto connection = eventThreadFor(cycle).createEventConnection(eventRegistration); const auto layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle)); if (layerId != static_cast<int32_t>(UNASSIGNED_LAYER_ID)) { @@ -355,74 +379,51 @@ sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( return connection; } -sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) { - std::lock_guard<std::mutex> lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle, nullptr); - return mConnections[handle].connection; -} - -void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId, - bool connected) { - android::EventThread* thread; - { - std::lock_guard<std::mutex> lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); +void Scheduler::dispatchHotplug(PhysicalDisplayId displayId, Hotplug hotplug) { + if (hasEventThreads()) { + const bool connected = hotplug == Hotplug::Connected; + eventThreadFor(Cycle::Render).onHotplugReceived(displayId, connected); + eventThreadFor(Cycle::LastComposite).onHotplugReceived(displayId, connected); } - - thread->onHotplugReceived(displayId, connected); } -void Scheduler::onHotplugConnectionError(ConnectionHandle handle, int32_t errorCode) { - android::EventThread* thread; - { - std::lock_guard<std::mutex> lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); +void Scheduler::dispatchHotplugError(int32_t errorCode) { + if (hasEventThreads()) { + eventThreadFor(Cycle::Render).onHotplugConnectionError(errorCode); + eventThreadFor(Cycle::LastComposite).onHotplugConnectionError(errorCode); } - - thread->onHotplugConnectionError(errorCode); } void Scheduler::enableSyntheticVsync(bool enable) { - // TODO(b/241285945): Remove connection handles. - const ConnectionHandle handle = mAppConnectionHandle; - android::EventThread* thread; - { - std::lock_guard<std::mutex> lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); - } - thread->enableSyntheticVsync(enable); + eventThreadFor(Cycle::Render).enableSyntheticVsync(enable); } -void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) { +void Scheduler::onFrameRateOverridesChanged(Cycle cycle, PhysicalDisplayId displayId) { const bool supportsFrameRateOverrideByContent = pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent(); std::vector<FrameRateOverride> overrides = mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent); - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); - } - thread->onFrameRateOverridesChanged(displayId, std::move(overrides)); + eventThreadFor(cycle).onFrameRateOverridesChanged(displayId, std::move(overrides)); } -void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { +void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId, + int32_t connectedLevel, int32_t maxLevel) { + eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel); +} + +void Scheduler::onPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) { { std::lock_guard<std::mutex> lock(mPolicyLock); // Cache the last reported modes for primary display. - mPolicy.cachedModeChangedParams = {handle, mode}; + mPolicy.cachedModeChangedParams = {cycle, mode}; // Invalidate content based refresh rate selection so it could be calculated // again for the new refresh rate. mPolicy.contentRequirements.clear(); } - onNonPrimaryDisplayModeChanged(handle, mode); + onNonPrimaryDisplayModeChanged(cycle, mode); } void Scheduler::dispatchCachedReportedMode() { @@ -449,50 +450,51 @@ void Scheduler::dispatchCachedReportedMode() { } mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt; - onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle, + onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->cycle, mPolicy.cachedModeChangedParams->mode); } -void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { - android::EventThread* thread; - { - std::lock_guard<std::mutex> lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); +void Scheduler::onNonPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) { + if (hasEventThreads()) { + eventThreadFor(cycle).onModeChanged(mode); } - thread->onModeChanged(mode); } -void Scheduler::dump(ConnectionHandle handle, std::string& result) const { - android::EventThread* thread; - { - std::lock_guard<std::mutex> lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections.at(handle).thread.get(); - } - thread->dump(result); +void Scheduler::dump(Cycle cycle, std::string& result) const { + eventThreadFor(cycle).dump(result); } -void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration, +void Scheduler::setDuration(Cycle cycle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { - android::EventThread* thread; - { - std::lock_guard<std::mutex> lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); + if (hasEventThreads()) { + eventThreadFor(cycle).setDuration(workDuration, readyDuration); } - thread->setDuration(workDuration, readyDuration); } -void Scheduler::setVsyncConfigSet(const VsyncConfigSet& configs, Period vsyncPeriod) { - setVsyncConfig(mVsyncModulator->setVsyncConfigSet(configs), vsyncPeriod); +void Scheduler::updatePhaseConfiguration(Fps refreshRate) { + mRefreshRateStats->setRefreshRate(refreshRate); + mVsyncConfiguration->setRefreshRateFps(refreshRate); + setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()), + refreshRate.getPeriod()); +} + +void Scheduler::resetPhaseConfiguration(Fps refreshRate) { + // Cancel the pending refresh rate change, if any, before updating the phase configuration. + mVsyncModulator->cancelRefreshRateChange(); + + mVsyncConfiguration->reset(); + updatePhaseConfiguration(refreshRate); +} + +void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) { + mRefreshRateStats->setPowerMode(powerMode); } void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) { - setDuration(mAppConnectionHandle, + setDuration(Cycle::Render, /* workDuration */ config.appWorkDuration, /* readyDuration */ config.sfWorkDuration); - setDuration(mSfConnectionHandle, + setDuration(Cycle::LastComposite, /* workDuration */ vsyncPeriod, /* readyDuration */ config.sfWorkDuration); setDuration(config.sfWorkDuration); @@ -549,7 +551,7 @@ void Scheduler::onHardwareVsyncRequest(PhysicalDisplayId id, bool enabled) { // On main thread to serialize reads/writes of pending hardware VSYNC state. static_cast<void>( - schedule([=]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) { + schedule([=, this]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) { ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str()); if (const auto displayOpt = mDisplays.get(id)) { @@ -598,11 +600,13 @@ Fps Scheduler::getNextFrameInterval(PhysicalDisplayId id, return Fps{}; } const Display& display = *displayOpt; - const nsecs_t threshold = - display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriodNsecs() / 2; - const nsecs_t nextVsyncTime = display.schedulePtr->getTracker().nextAnticipatedVSyncTimeFrom( - currentExpectedPresentTime.ns() + threshold); - return Fps::fromPeriodNsecs(nextVsyncTime - currentExpectedPresentTime.ns()); + const Duration threshold = + display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriod() / 2; + const TimePoint nextVsyncTime = + display.schedulePtr->vsyncDeadlineAfter(currentExpectedPresentTime + threshold, + currentExpectedPresentTime); + const Duration frameInterval = nextVsyncTime - currentExpectedPresentTime; + return Fps::fromPeriodNsecs(frameInterval.ns()); } void Scheduler::resync() { @@ -856,6 +860,12 @@ void Scheduler::dump(utils::Dumper& dumper) const { mFrameRateOverrideMappings.dump(dumper); dumper.eol(); + mVsyncConfiguration->dump(dumper.out()); + dumper.eol(); + + mRefreshRateStats->dump(dumper.out()); + dumper.eol(); + { utils::Dumper::Section section(dumper, "Frame Targeting"sv); @@ -951,16 +961,10 @@ std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked( void Scheduler::applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule> vsyncSchedule) { onNewVsyncSchedule(vsyncSchedule->getDispatch()); - std::vector<android::EventThread*> threads; - { - std::lock_guard<std::mutex> lock(mConnectionsLock); - threads.reserve(mConnections.size()); - for (auto& [_, connection] : mConnections) { - threads.push_back(connection.thread.get()); - } - } - for (auto* thread : threads) { - thread->onNewVsyncSchedule(vsyncSchedule); + + if (hasEventThreads()) { + eventThreadFor(Cycle::Render).onNewVsyncSchedule(vsyncSchedule); + eventThreadFor(Cycle::LastComposite).onNewVsyncSchedule(vsyncSchedule); } } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index ce585c624a..09f75fdaca 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -56,55 +56,38 @@ #include <FrontEnd/LayerHierarchy.h> -namespace android::scheduler { - -// Opaque handle to scheduler connection. -struct ConnectionHandle { - using Id = std::uintptr_t; - static constexpr Id INVALID_ID = static_cast<Id>(-1); - - Id id = INVALID_ID; - - explicit operator bool() const { return id != INVALID_ID; } -}; - -inline bool operator==(ConnectionHandle lhs, ConnectionHandle rhs) { - return lhs.id == rhs.id; -} - -} // namespace android::scheduler - -namespace std { - -template <> -struct hash<android::scheduler::ConnectionHandle> { - size_t operator()(android::scheduler::ConnectionHandle handle) const { - return hash<android::scheduler::ConnectionHandle::Id>()(handle.id); - } -}; - -} // namespace std - namespace android { class FenceTime; +class TimeStats; namespace frametimeline { class TokenManager; } // namespace frametimeline +namespace surfaceflinger { +class Factory; +} // namespace surfaceflinger + namespace scheduler { using GlobalSignals = RefreshRateSelector::GlobalSignals; +class RefreshRateStats; +class VsyncConfiguration; class VsyncSchedule; +enum class Cycle { + Render, // Surface rendering. + LastComposite // Ahead of display compositing by one refresh period. +}; + class Scheduler : public IEventThreadCallback, android::impl::MessageQueue { using Impl = android::impl::MessageQueue; public: - Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>, - IVsyncTrackerCallback&); + Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, surfaceflinger::Factory&, + Fps activeRefreshRate, TimeStats&); virtual ~Scheduler(); void startTimers(); @@ -124,11 +107,11 @@ public: void run(); - using Impl::initVsync; + void initVsync(frametimeline::TokenManager&, std::chrono::nanoseconds workDuration); - using Impl::getScheduledFrameTime; using Impl::setDuration; + using Impl::getScheduledFrameResult; using Impl::scheduleConfigure; using Impl::scheduleFrame; @@ -147,34 +130,34 @@ public: return std::move(future); } - enum class Cycle { - Render, // Surface rendering. - LastComposite // Ahead of display compositing by one refresh period. - }; - - ConnectionHandle createEventThread(Cycle, frametimeline::TokenManager*, - std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration); + void createEventThread(Cycle, frametimeline::TokenManager*, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration); sp<IDisplayEventConnection> createDisplayEventConnection( - ConnectionHandle, EventRegistrationFlags eventRegistration = {}, + Cycle, EventRegistrationFlags eventRegistration = {}, const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock); - sp<EventThreadConnection> getEventConnection(ConnectionHandle); + const sp<EventThreadConnection>& getEventConnection(Cycle cycle) const { + return cycle == Cycle::Render ? mRenderEventConnection : mLastCompositeEventConnection; + } + + enum class Hotplug { Connected, Disconnected }; + void dispatchHotplug(PhysicalDisplayId, Hotplug); - void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); - void onHotplugConnectionError(ConnectionHandle, int32_t errorCode); + void dispatchHotplugError(int32_t errorCode); - void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock); - void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&); + void onPrimaryDisplayModeChanged(Cycle, const FrameRateMode&) EXCLUDES(mPolicyLock); + void onNonPrimaryDisplayModeChanged(Cycle, const FrameRateMode&); void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext); - void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId) - EXCLUDES(mConnectionsLock); + void onFrameRateOverridesChanged(Cycle, PhysicalDisplayId); + + void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t); // Modifies work duration in the event thread. - void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration, + void setDuration(Cycle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration); VsyncModulator& vsyncModulator() { return *mVsyncModulator; } @@ -199,7 +182,10 @@ public: } } - void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod); + void updatePhaseConfiguration(Fps); + void resetPhaseConfiguration(Fps) REQUIRES(kMainThreadContext); + + const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; } // Sets the render rate for the scheduler to run at. void setRenderRate(PhysicalDisplayId, Fps); @@ -247,8 +233,10 @@ public: // Indicates that touch interaction is taking place. void onTouchHint(); - void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode) - REQUIRES(kMainThreadContext); + void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode) REQUIRES(kMainThreadContext); + + // TODO(b/255635821): Track this per display. + void setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode) REQUIRES(kMainThreadContext); ConstVsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> = std::nullopt) const EXCLUDES(mDisplayLock); @@ -274,7 +262,7 @@ public: bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const; void dump(utils::Dumper&) const; - void dump(ConnectionHandle, std::string&) const; + void dump(Cycle, std::string&) const; void dumpVsync(std::string&) const EXCLUDES(mDisplayLock); // Returns the preferred refresh rate and frame rate for the pacesetter display. @@ -355,8 +343,15 @@ private: void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override REQUIRES(kMainThreadContext, mDisplayLock); - // Create a connection on the given EventThread. - ConnectionHandle createConnection(std::unique_ptr<EventThread>); + // Used to skip event dispatch before EventThread creation during boot. + // TODO: b/241285191 - Reorder Scheduler initialization to avoid this. + bool hasEventThreads() const { + return CC_LIKELY(mRenderEventThread && mLastCompositeEventThread); + } + + EventThread& eventThreadFor(Cycle cycle) const { + return *(cycle == Cycle::Render ? mRenderEventThread : mLastCompositeEventThread); + } // Update feature state machine to given state when corresponding timer resets or expires. void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock); @@ -444,27 +439,26 @@ private: bool throttleVsync(TimePoint, uid_t) override; Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); void resync() override EXCLUDES(mDisplayLock); + void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock); - // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. - struct Connection { - sp<EventThreadConnection> connection; - std::unique_ptr<EventThread> thread; - }; - - ConnectionHandle::Id mNextConnectionHandleId = 0; - mutable std::mutex mConnectionsLock; - std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock); + std::unique_ptr<EventThread> mRenderEventThread; + sp<EventThreadConnection> mRenderEventConnection; - ConnectionHandle mAppConnectionHandle; - ConnectionHandle mSfConnectionHandle; + std::unique_ptr<EventThread> mLastCompositeEventThread; + sp<EventThreadConnection> mLastCompositeEventConnection; std::atomic<nsecs_t> mLastResyncTime = 0; const FeatureFlags mFeatures; + // Stores phase offsets configured per refresh rate. + const std::unique_ptr<VsyncConfiguration> mVsyncConfiguration; + // Shifts the VSYNC phase during certain transactions and refresh rate changes. const sp<VsyncModulator> mVsyncModulator; + const std::unique_ptr<RefreshRateStats> mRefreshRateStats; + // Used to choose refresh rate if content detection is enabled. LayerHistory mLayerHistory; @@ -478,8 +472,6 @@ private: ISchedulerCallback& mSchedulerCallback; - IVsyncTrackerCallback& mVsyncTrackerCallback; - // mDisplayLock may be locked while under mPolicyLock. mutable std::mutex mPolicyLock; @@ -495,9 +487,7 @@ private: : displayId(displayId), selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)), - targeterPtr(std::make_unique< - FrameTargeter>(displayId, - features.test(Feature::kBackpressureGpuComposition))) {} + targeterPtr(std::make_unique<FrameTargeter>(displayId, features)) {} const PhysicalDisplayId displayId; @@ -569,7 +559,7 @@ private: ftl::Optional<FrameRateMode> modeOpt; struct ModeChangedParams { - ConnectionHandle handle; + Cycle cycle; FrameRateMode mode; }; diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h deleted file mode 100644 index a05c123956..0000000000 --- a/services/surfaceflinger/Scheduler/StrongTyping.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2019 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 -namespace android { - -template <typename T, template <typename> class AbilityType> -struct Ability { - T& base() { return static_cast<T&>(*this); } - T const& base() const { return static_cast<T const&>(*this); } -}; - -template <typename T> -struct Add : Ability<T, Add> { - inline T operator+(T const& other) const { return T(this->base().value() + other.value()); } - inline T& operator++() { - ++this->base().value(); - return this->base(); - }; - inline T operator++(int) { - T tmp(this->base()); - operator++(); - return tmp; - }; - inline T& operator+=(T const& other) { - this->base().value() += other.value(); - return this->base(); - }; -}; - -template <typename T> -struct Compare : Ability<T, Compare> { - inline bool operator==(T const& other) const { return this->base().value() == other.value(); }; - inline bool operator<(T const& other) const { return this->base().value() < other.value(); } - inline bool operator<=(T const& other) const { return (*this < other) || (*this == other); } - inline bool operator!=(T const& other) const { return !(*this == other); } - inline bool operator>=(T const& other) const { return !(*this < other); } - inline bool operator>(T const& other) const { return !(*this < other || *this == other); } -}; - -template <typename T> -struct Hash : Ability<T, Hash> { - [[nodiscard]] std::size_t hash() const { - return std::hash<typename std::remove_const< - typename std::remove_reference<decltype(this->base().value())>::type>::type>{}( - this->base().value()); - } -}; - -template <typename T, typename W, template <typename> class... Ability> -struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... { - constexpr StrongTyping() = default; - constexpr explicit StrongTyping(T const& value) : mValue(value) {} - StrongTyping(StrongTyping const&) = default; - StrongTyping& operator=(StrongTyping const&) = default; - explicit inline operator T() const { return mValue; } - T const& value() const { return mValue; } - T& value() { return mValue; } - - friend std::ostream& operator<<(std::ostream& os, const StrongTyping<T, W, Ability...>& value) { - return os << value.value(); - } - -private: - T mValue{0}; -}; -} // namespace android - -namespace std { -template <typename T, typename W, template <typename> class... Ability> -struct hash<android::StrongTyping<T, W, Ability...>> { - std::size_t operator()(android::StrongTyping<T, W, Ability...> const& k) const { - return k.hash(); - } -}; -} // namespace std diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index c3a952f689..0c43ffbc04 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -20,13 +20,16 @@ #include <optional> #include <string> +#include <ftl/mixins.h> +#include <scheduler/Time.h> #include <utils/Timers.h> -#include "StrongTyping.h" - namespace android::scheduler { -using ScheduleResult = std::optional<nsecs_t>; +struct ScheduleResult { + TimePoint callbackTime; + TimePoint vsyncTime; +}; enum class CancelResult { Cancelled, TooLate, Error }; @@ -35,7 +38,11 @@ enum class CancelResult { Cancelled, TooLate, Error }; */ class VSyncDispatch { public: - using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>; + struct CallbackToken : ftl::DefaultConstructible<CallbackToken, size_t>, + ftl::Equatable<CallbackToken>, + ftl::Incrementable<CallbackToken> { + using DefaultConstructible::DefaultConstructible; + }; virtual ~VSyncDispatch(); @@ -84,8 +91,8 @@ public: * able to provide the ready-by time (deadline) on the callback. * For internal clients, we don't need to add additional padding, so * readyDuration will typically be 0. - * @earliestVsync: The targeted display time. This will be snapped to the closest - * predicted vsync time after earliestVsync. + * @lastVsync: The targeted display time. This will be snapped to the closest + * predicted vsync time after lastVsync. * * callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync * event. @@ -93,11 +100,11 @@ public: struct ScheduleTiming { nsecs_t workDuration = 0; nsecs_t readyDuration = 0; - nsecs_t earliestVsync = 0; + nsecs_t lastVsync = 0; bool operator==(const ScheduleTiming& other) const { return workDuration == other.workDuration && readyDuration == other.readyDuration && - earliestVsync == other.earliestVsync; + lastVsync == other.lastVsync; } bool operator!=(const ScheduleTiming& other) const { return !(*this == other); } @@ -109,22 +116,24 @@ public: * The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync * event. * - * The caller designates the earliest vsync event that should be targeted by the earliestVsync + * The caller designates the earliest vsync event that should be targeted by the lastVsync * parameter. * The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where - * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ). + * predictedVsync is the first vsync event time where ( predictedVsync >= lastVsync ). * - * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has + * If (workDuration + readyDuration - lastVsync) is in the past, or if a callback has * already been dispatched for the predictedVsync, an error will be returned. * * It is valid to reschedule a callback to a different time. * * \param [in] token The callback to schedule. * \param [in] scheduleTiming The timing information for this schedule call - * \return The expected callback time if a callback was scheduled. + * \return The expected callback time if a callback was scheduled, + * along with VSYNC time for the callback scheduled. * std::nullopt if the callback is not registered. */ - virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0; + virtual std::optional<ScheduleResult> schedule(CallbackToken token, + ScheduleTiming scheduleTiming) = 0; /* * Update the timing information for a scheduled callback. @@ -132,10 +141,12 @@ public: * * \param [in] token The callback to schedule. * \param [in] scheduleTiming The timing information for this schedule call - * \return The expected callback time if a callback was scheduled. + * \return The expected callback time if a callback was scheduled, + * along with VSYNC time for the callback scheduled. * std::nullopt if the callback is not registered. */ - virtual ScheduleResult update(CallbackToken token, ScheduleTiming scheduleTiming) = 0; + virtual std::optional<ScheduleResult> update(CallbackToken token, + ScheduleTiming scheduleTiming) = 0; /* Cancels a scheduled callback, if possible. * @@ -165,10 +176,10 @@ public: VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&); // See documentation for VSyncDispatch::schedule. - ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming); + std::optional<ScheduleResult> schedule(VSyncDispatch::ScheduleTiming scheduleTiming); // See documentation for VSyncDispatch::update. - ScheduleResult update(VSyncDispatch::ScheduleTiming scheduleTiming); + std::optional<ScheduleResult> update(VSyncDispatch::ScheduleTiming scheduleTiming); // See documentation for VSyncDispatch::cancel. CancelResult cancel(); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index ef30887037..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> @@ -38,16 +38,21 @@ using base::StringAppendF; namespace { -nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime, - const VSyncDispatch::ScheduleTiming& timing) { - return nextVsyncTime - timing.readyDuration - timing.workDuration; +ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime, + const VSyncDispatch::ScheduleTiming& timing) { + return {TimePoint::fromNs(nextVsyncTime - timing.readyDuration - timing.workDuration), + TimePoint::fromNs(nextVsyncTime)}; } -nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now, - const VSyncDispatch::ScheduleTiming& timing) { - const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom( - std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration)); - return getExpectedCallbackTime(nextVsyncTime, timing); +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 @@ -93,15 +98,21 @@ std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const { ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker, nsecs_t now) { - auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom( - std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration)); + ATRACE_NAME("VSyncDispatchTimerQueueEntry::schedule"); + auto nextVsyncTime = + tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync, + now + timing.workDuration + + timing.readyDuration), + timing.lastVsync); auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; bool const wouldSkipAVsyncTarget = mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance)); bool const wouldSkipAWakeup = mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance))); - if (FlagManager::getInstance().dont_skip_on_early()) { + 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; } else { @@ -119,11 +130,15 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim auto const nextReadyTime = nextVsyncTime - timing.readyDuration; mScheduleTiming = timing; mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime}; - return nextWakeupTime; + return ScheduleResult{TimePoint::fromNs(nextWakeupTime), TimePoint::fromNs(nextVsyncTime)}; } -void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) { +ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate( + VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) { mWorkloadUpdateInfo = timing; + const auto armedInfo = getArmedInfo(tracker, now, timing, mArmedInfo); + return {TimePoint::fromNs(armedInfo.mActualWakeupTime), + TimePoint::fromNs(armedInfo.mActualVsyncTime)}; } bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const { @@ -139,36 +154,68 @@ nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker, bool const nextVsyncTooClose = mLastDispatchTime && (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod; if (alreadyDispatchedForVsync) { - return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance); + ATRACE_FORMAT_INSTANT("alreadyDispatchedForVsync"); + return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance, + *mLastDispatchTime); } if (nextVsyncTooClose) { - return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod); + ATRACE_FORMAT_INSTANT("nextVsyncTooClose"); + return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod, + *mLastDispatchTime + currentPeriod); } return nextVsyncTime; } +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); + + const auto nextVsyncTime = + adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/ + tracker.nextAnticipatedVSyncTimeFrom(earliestVsync, + timing.lastVsync)); + const auto nextReadyTime = nextVsyncTime - timing.readyDuration; + const auto nextWakeupTime = nextReadyTime - timing.workDuration; + + 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(); } - const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration; - const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync); - - const auto nextVsyncTime = - adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/ - tracker.nextAnticipatedVSyncTimeFrom(earliestVsync)); - const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration; - const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration; - - mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime}; + mArmedInfo = getArmedInfo(tracker, now, mScheduleTiming, mArmedInfo); } void VSyncDispatchTimerQueueEntry::disarm() { @@ -214,10 +261,10 @@ void VSyncDispatchTimerQueueEntry::dump(std::string& result) const { StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(), mRunning ? "(in callback function)" : "", armedInfo.c_str()); StringAppendF(&result, - "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative " + "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative " "to now\n", mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f, - (mScheduleTiming.earliestVsync - systemTime()) / 1e6f); + (mScheduleTiming.lastVsync - systemTime()) / 1e6f); if (mLastDispatchTime) { StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n", @@ -237,6 +284,7 @@ VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() { std::lock_guard lock(mMutex); + mRunning = false; cancelTimer(); for (auto& [_, entry] : mCallbacks) { ALOGE("Forgot to unregister a callback on VSyncDispatch!"); @@ -257,15 +305,16 @@ void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) { } void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) { - rearmTimerSkippingUpdateFor(now, mCallbacks.end()); + rearmTimerSkippingUpdateFor(now, mCallbacks.cend()); } void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( - nsecs_t now, CallbackMap::iterator const& skipUpdateIt) { + 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; - for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { + for (auto it = mCallbacks.cbegin(); it != mCallbacks.cend(); ++it) { auto& callback = it->second; if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) { continue; @@ -274,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; @@ -283,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"); @@ -296,6 +343,7 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( } void VSyncDispatchTimerQueue::timerCallback() { + ATRACE_CALL(); struct Invocation { std::shared_ptr<VSyncDispatchTimerQueueEntry> callback; nsecs_t vsyncTimestamp; @@ -305,6 +353,10 @@ void VSyncDispatchTimerQueue::timerCallback() { std::vector<Invocation> invocations; { std::lock_guard lock(mMutex); + if (!mRunning) { + ALOGD("TimerQueue is not running. Skipping callback."); + return; + } auto const now = mTimeKeeper->now(); mLastTimerCallback = now; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { @@ -314,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(); @@ -329,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); } @@ -337,13 +392,12 @@ void VSyncDispatchTimerQueue::timerCallback() { VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback( Callback callback, std::string callbackName) { std::lock_guard lock(mMutex); - return CallbackToken{ - mCallbacks - .emplace(++mCallbackToken, - std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName), - std::move(callback), - mMinVsyncDistance)) - .first->first}; + return mCallbacks + .try_emplace(++mCallbackToken, + std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName), + std::move(callback), + mMinVsyncDistance)) + .first->first; } void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { @@ -353,7 +407,7 @@ void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { auto it = mCallbacks.find(token); if (it != mCallbacks.end()) { entry = it->second; - mCallbacks.erase(it); + mCallbacks.erase(it->first); } } @@ -362,14 +416,14 @@ void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { } } -ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, - ScheduleTiming scheduleTiming) { +std::optional<ScheduleResult> VSyncDispatchTimerQueue::schedule(CallbackToken token, + ScheduleTiming scheduleTiming) { std::lock_guard lock(mMutex); return scheduleLocked(token, scheduleTiming); } -ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token, - ScheduleTiming scheduleTiming) { +std::optional<ScheduleResult> VSyncDispatchTimerQueue::scheduleLocked( + CallbackToken token, ScheduleTiming scheduleTiming) { auto it = mCallbacks.find(token); if (it == mCallbacks.end()) { return {}; @@ -381,14 +435,10 @@ ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token, * timer recalculation to avoid cancelling a callback that is about to fire. */ auto const rearmImminent = now > mIntendedWakeupTime; if (CC_UNLIKELY(rearmImminent)) { - callback->addPendingWorkloadUpdate(scheduleTiming); - return getExpectedCallbackTime(*mTracker, now, scheduleTiming); + return callback->addPendingWorkloadUpdate(*mTracker, now, scheduleTiming); } - const ScheduleResult result = callback->schedule(scheduleTiming, *mTracker, now); - if (!result.has_value()) { - return {}; - } + const auto result = callback->schedule(scheduleTiming, *mTracker, now); if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) { rearmTimerSkippingUpdateFor(now, it); @@ -397,7 +447,8 @@ ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token, return result; } -ScheduleResult VSyncDispatchTimerQueue::update(CallbackToken token, ScheduleTiming scheduleTiming) { +std::optional<ScheduleResult> VSyncDispatchTimerQueue::update(CallbackToken token, + ScheduleTiming scheduleTiming) { std::lock_guard lock(mMutex); const auto it = mCallbacks.find(token); if (it == mCallbacks.end()) { @@ -474,14 +525,16 @@ VSyncCallbackRegistration::~VSyncCallbackRegistration() { if (mToken) mDispatch->unregisterCallback(*mToken); } -ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) { +std::optional<ScheduleResult> VSyncCallbackRegistration::schedule( + VSyncDispatch::ScheduleTiming scheduleTiming) { if (!mToken) { return std::nullopt; } return mDispatch->schedule(*mToken, scheduleTiming); } -ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) { +std::optional<ScheduleResult> VSyncCallbackRegistration::update( + VSyncDispatch::ScheduleTiming scheduleTiming) { if (!mToken) { return std::nullopt; } diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h index e0fb8f9f86..e4ddc03480 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -16,14 +16,13 @@ #pragma once -#include <functional> #include <memory> #include <mutex> #include <string> #include <string_view> -#include <unordered_map> #include <android-base/thread_annotations.h> +#include <ftl/small_map.h> #include "VSyncDispatch.h" #include "VsyncSchedule.h" @@ -70,7 +69,8 @@ public: // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next // call to update() - void addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming); + ScheduleResult addPendingWorkloadUpdate(VSyncTracker&, nsecs_t now, + VSyncDispatch::ScheduleTiming); // Checks if there is a pending update to the workload, returning true if so. bool hasPendingWorkloadUpdate() const; @@ -84,7 +84,15 @@ public: void dump(std::string& result) const; private: + struct ArmingInfo { + nsecs_t mActualWakeupTime; + nsecs_t mActualVsyncTime; + nsecs_t mActualReadyTime; + }; + nsecs_t adjustVsyncIfNeeded(VSyncTracker& tracker, nsecs_t nextVsyncTime) const; + ArmingInfo getArmedInfo(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming, + std::optional<ArmingInfo>) const; const std::string mName; const VSyncDispatch::Callback mCallback; @@ -92,11 +100,6 @@ private: VSyncDispatch::ScheduleTiming mScheduleTiming; const nsecs_t mMinVsyncDistance; - struct ArmingInfo { - nsecs_t mActualWakeupTime; - nsecs_t mActualVsyncTime; - nsecs_t mActualReadyTime; - }; std::optional<ArmingInfo> mArmedInfo; std::optional<nsecs_t> mLastDispatchTime; @@ -126,8 +129,8 @@ public: CallbackToken registerCallback(Callback, std::string callbackName) final; void unregisterCallback(CallbackToken) final; - ScheduleResult schedule(CallbackToken, ScheduleTiming) final; - ScheduleResult update(CallbackToken, ScheduleTiming) final; + std::optional<ScheduleResult> schedule(CallbackToken, ScheduleTiming) final; + std::optional<ScheduleResult> update(CallbackToken, ScheduleTiming) final; CancelResult cancel(CallbackToken) final; void dump(std::string&) const final; @@ -135,26 +138,31 @@ private: VSyncDispatchTimerQueue(const VSyncDispatchTimerQueue&) = delete; VSyncDispatchTimerQueue& operator=(const VSyncDispatchTimerQueue&) = delete; + // The static capacity was chosen to exceed the expected number of callbacks. using CallbackMap = - std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>; + ftl::SmallMap<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>, 5>; void timerCallback(); void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex); void rearmTimer(nsecs_t now) REQUIRES(mMutex); - void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate) + void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::const_iterator skipUpdate) REQUIRES(mMutex); void cancelTimer() REQUIRES(mMutex); - ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex); + std::optional<ScheduleResult> scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex); std::mutex mutable mMutex; + // During VSyncDispatchTimerQueue deconstruction, skip timerCallback to + // avoid crash + bool mRunning = true; + static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); std::unique_ptr<TimeKeeper> const mTimeKeeper; VsyncSchedule::TrackerPtr mTracker; nsecs_t const mTimerSlack; nsecs_t const mMinVsyncDistance; - size_t mCallbackToken GUARDED_BY(mMutex) = 0; + CallbackToken mCallbackToken GUARDED_BY(mMutex); CallbackMap mCallbacks GUARDED_BY(mMutex); nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 0d79a390a7..2f9dfeaff7 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -45,17 +45,39 @@ using base::StringAppendF; static auto constexpr kMaxPercent = 100u; +namespace { +nsecs_t getVsyncFixup(VSyncPredictor::Model model, Period minFramePeriod, nsecs_t vsyncTime, + std::optional<nsecs_t> lastVsyncOpt) { + const auto threshold = model.slope / 2; + + if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) { + const auto vsyncDiff = vsyncTime - *lastVsyncOpt; + if (vsyncDiff >= threshold && vsyncDiff <= minFramePeriod.ns() - threshold) { + const auto vsyncFixup = *lastVsyncOpt + minFramePeriod.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>(vsyncFixup) / 1e6f); + return vsyncFixup; + } + } + + return 0; +} +} // namespace + VSyncPredictor::~VSyncPredictor() = default; -VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, - IVsyncTrackerCallback& callback) - : 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)), - mVsyncTrackerCallback(callback), mDisplayModePtr(modePtr) { resetModel(); } @@ -149,7 +171,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; } @@ -252,18 +274,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { return true; } -auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence { - const auto vsync = nextAnticipatedVSyncTimeFromLocked(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::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const { +nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const { auto const [slope, intercept] = getVSyncPredictionModelLocked(); if (mTimestamps.empty()) { @@ -299,56 +310,33 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) co return prediction; } -nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { +nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, + std::optional<nsecs_t> lastVsyncOpt) { + ATRACE_CALL(); std::lock_guard lock(mMutex); - // update the mLastVsyncSequence for reference point - mLastVsyncSequence = getVsyncSequenceLocked(timePoint); - - const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int { - if (!mRenderRateOpt) return 0; - const auto divisor = - RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()), - *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; - }(); + const auto now = TimePoint::fromNs(mClock->now()); + purgeTimelines(now); - if (renderRatePhase == 0) { - const auto vsyncTime = mLastVsyncSequence->vsyncTime; - if (FlagManager::getInstance().vrr_config()) { - const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime); - ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__, - ticks<std::milli, float>(vsyncTimePoint - TimePoint::now())); - const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); - mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate); + std::optional<TimePoint> vsyncOpt; + for (auto& timeline : mTimelines) { + vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(getVSyncPredictionModelLocked(), + minFramePeriodLocked(), + snapToVsync(timePoint), mMissedVsync, + lastVsyncOpt); + if (vsyncOpt) { + break; } - return vsyncTime; } + LOG_ALWAYS_FATAL_IF(!vsyncOpt); - auto const [slope, intercept] = getVSyncPredictionModelLocked(); - const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase; - const auto nextAnticipatedVsyncTime = - nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); - if (FlagManager::getInstance().vrr_config()) { - const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime); - ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__, - ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now())); - const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); - mVsyncTrackerCallback.onVsyncGenerated(nextAnticipatedVsyncTimePoint, mDisplayModePtr, - renderRate); + if (*vsyncOpt > mLastCommittedVsync) { + mLastCommittedVsync = *vsyncOpt; + ATRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms", + float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f); } - return nextAnticipatedVsyncTime; + + return vsyncOpt->ns(); } /* @@ -359,32 +347,28 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { * 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) { @@ -392,6 +376,9 @@ void VSyncPredictor::setRenderRate(Fps renderRate) { ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str()); std::lock_guard lock(mMutex); mRenderRateOpt = renderRate; + mTimelines.back().freeze(TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2)); + mTimelines.emplace_back(mIdealPeriod, renderRate); + purgeTimelines(TimePoint::fromNs(mClock->now())); } void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { @@ -403,7 +390,7 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { : std::nullopt; ALOGV("%s %s: DisplayMode %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(), to_string(*modePtr).c_str(), - timeout ? std::to_string(timeout->notifyExpectedPresentTimeoutNs).c_str() : "N/A"); + timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A"); std::lock_guard lock(mMutex); mDisplayModePtr = modePtr; @@ -421,8 +408,9 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { clearTimestamps(); } -void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, - TimePoint lastConfirmedPresentTime) { +Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, + TimePoint lastConfirmedPresentTime) { + ATRACE_CALL(); const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; const auto threshold = currentPeriod / 2; const auto minFramePeriod = minFramePeriodLocked().ns(); @@ -448,16 +436,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; @@ -468,23 +460,17 @@ void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, lastConfirmedPresentTime.ns()) / 1e6f); } - mPastExpectedPresentTimes.push_back(expectedPresentTime); - const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; const auto threshold = currentPeriod / 2; + mPastExpectedPresentTimes.push_back(expectedPresentTime); - const auto minFramePeriod = minFramePeriodLocked().ns(); while (!mPastExpectedPresentTimes.empty()) { const auto front = mPastExpectedPresentTimes.front().ns(); - const bool frontIsLastConfirmed = - std::abs(front - lastConfirmedPresentTime.ns()) < threshold; - const bool frontIsBeforeConfirmed = - front < lastConfirmedPresentTime.ns() - minFramePeriod + threshold; - if (frontIsLastConfirmed || frontIsBeforeConfirmed) { + const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold; + if (frontIsBeforeConfirmed) { if (CC_UNLIKELY(mTraceOn)) { ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence", - static_cast<float>(lastConfirmedPresentTime.ns() - - mPastExpectedPresentTimes.front().ns()) / + static_cast<float>(lastConfirmedPresentTime.ns() - front) / 1e6f); } mPastExpectedPresentTimes.pop_front(); @@ -493,11 +479,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; @@ -507,13 +496,15 @@ void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) { const auto lastConfirmedPresentTime = TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod); - ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); + 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 { @@ -534,6 +525,11 @@ void VSyncPredictor::clearTimestamps() { mTimestamps.clear(); mLastTimestampIndex = 0; } + + mTimelines.clear(); + mLastCommittedVsync = TimePoint::fromNs(0); + mIdealPeriod = Period::fromNs(idealPeriod()); + mTimelines.emplace_back(mIdealPeriod, mRenderRateOpt); } bool VSyncPredictor::needsMoreSamples() const { @@ -557,6 +553,130 @@ 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) { + 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()); +} + +VSyncPredictor::VsyncTimeline::VsyncTimeline(Period idealPeriod, std::optional<Fps> renderRateOpt) + : mIdealPeriod(idealPeriod), mRenderRateOpt(renderRateOpt) {} + +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, Period minFramePeriod, nsecs_t vsync, MissedVsync missedVsync, + std::optional<nsecs_t> lastVsyncOpt) { + ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA"); + + const auto threshold = model.slope / 2; + const auto lastFrameMissed = + lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold; + nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync); + nsecs_t vsyncFixupTime = 0; + if (FlagManager::getInstance().vrr_config() && lastFrameMissed) { + vsyncTime += missedVsync.fixup.ns(); + ATRACE_FORMAT_INSTANT("lastFrameMissed"); + } else { + vsyncFixupTime = getVsyncFixup(model, minFramePeriod, vsyncTime, lastVsyncOpt); + 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 (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 72a343112a..c175765485 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,21 +32,22 @@ 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. * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. - * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker. */ - VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, - IVsyncTrackerCallback&); + 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) const final EXCLUDES(mMutex); + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, + std::optional<nsecs_t> lastVsyncOpt = {}) final + EXCLUDES(mMutex); nsecs_t currentPeriod() const final EXCLUDES(mMutex); Period minFramePeriod() const final EXCLUDES(mMutex); void resetModel() final EXCLUDES(mMutex); @@ -62,7 +64,7 @@ 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); @@ -75,10 +77,42 @@ 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(Period idealPeriod, std::optional<Fps> renderRateOpt); + std::optional<TimePoint> nextAnticipatedVSyncTimeFrom( + Model model, Period minFramePeriod, 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); + + private: + nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync); + VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync); + + const Period mIdealPeriod = Duration::fromNs(0); + const 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; @@ -87,23 +121,17 @@ private: size_t next(size_t i) const REQUIRES(mMutex); bool validate(nsecs_t timestamp) const REQUIRES(mMutex); Model getVSyncPredictionModelLocked() const REQUIRES(mMutex); - nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex); - bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex); + nsecs_t snapToVsync(nsecs_t timePoint) 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; size_t const kHistorySize; size_t const kMinimumSamplesForPrediction; size_t const kOutlierTolerancePercent; - IVsyncTrackerCallback& mVsyncTrackerCallback; std::mutex mutable mMutex; std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); @@ -115,11 +143,15 @@ 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); std::deque<TimePoint> mPastExpectedPresentTimes 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 1ed863cf7d..1e55a87254 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -26,12 +26,6 @@ namespace android::scheduler { -struct IVsyncTrackerCallback { - virtual ~IVsyncTrackerCallback() = default; - virtual void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, - Fps renderRate) = 0; -}; - /* * VSyncTracker is an interface for providing estimates on future Vsync signal times based on * historical vsync timing data. @@ -57,9 +51,13 @@ public: * is updated. * * \param [in] timePoint The point in time after which to estimate a vsync event. + * \param [in] lastVsyncOpt The last vsync time used by the client. If provided, the tracker + * should use that as a reference point when generating the new vsync + * 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) const = 0; + virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, + std::optional<nsecs_t> lastVsyncOpt = {}) = 0; /* * The current period of the vsync signal. @@ -84,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. diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index db6a1871a7..2fa3318560 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -57,11 +57,10 @@ private: }; VsyncSchedule::VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags features, - RequestHardwareVsync requestHardwareVsync, - IVsyncTrackerCallback& callback) + RequestHardwareVsync requestHardwareVsync) : mId(modePtr->getPhysicalDisplayId()), mRequestHardwareVsync(std::move(requestHardwareVsync)), - mTracker(createTracker(modePtr, callback)), + mTracker(createTracker(modePtr)), mDispatch(createDispatch(mTracker)), mController(createController(modePtr->getPhysicalDisplayId(), *mTracker, features)), mTracer(features.test(Feature::kTracePredictedVsync) @@ -89,8 +88,12 @@ Period VsyncSchedule::minFramePeriod() const { return period(); } -TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const { - return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns())); +TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint, + ftl::Optional<TimePoint> lastVsyncOpt) const { + return TimePoint::fromNs( + mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns(), + lastVsyncOpt.transform( + [](TimePoint t) { return t.ns(); }))); } void VsyncSchedule::dump(std::string& out) const { @@ -111,15 +114,14 @@ void VsyncSchedule::dump(std::string& out) const { mDispatch->dump(out); } -VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr, - IVsyncTrackerCallback& callback) { +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr) { // TODO(b/144707443): Tune constants. constexpr size_t kHistorySize = 20; constexpr size_t kMinSamplesForPrediction = 6; constexpr uint32_t kDiscardOutlierPercent = 20; - return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction, - kDiscardOutlierPercent, callback); + 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 722ea0b836..881d6789b2 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -57,13 +57,13 @@ class VsyncSchedule final : public IVsyncSource { public: using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>; - VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync, - IVsyncTrackerCallback&); + VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync); ~VsyncSchedule(); // IVsyncSource overrides: Period period() const override; - TimePoint vsyncDeadlineAfter(TimePoint) const override; + TimePoint vsyncDeadlineAfter(TimePoint, + ftl::Optional<TimePoint> lastVsyncOpt = {}) const override; Period minFramePeriod() const override; // Inform the schedule that the display mode changed the schedule needs to recalibrate @@ -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; } @@ -127,7 +127,7 @@ private: friend class android::VsyncScheduleTest; friend class android::fuzz::SchedulerFuzzer; - static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback&); + static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr); static DispatchPtr createDispatch(TrackerPtr); static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags); diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h index 7c72ac6afc..52485fb4e9 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h @@ -29,6 +29,7 @@ enum class Feature : std::uint8_t { kTracePredictedVsync = 1 << 3, kBackpressureGpuComposition = 1 << 4, kSmallDirtyContentDetection = 1 << 5, + kExpectedPresentTime = 1 << 6, }; using FeatureFlags = ftl::Flags<Feature>; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h index 2806450c5f..84ef89fb4a 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h @@ -88,6 +88,7 @@ enum class FrameRateCategory : int32_t { NoPreference, Low, Normal, + HighHint, High, ftl_last = High diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h index 70d4846070..a5bb6c216f 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h @@ -19,11 +19,13 @@ #include <array> #include <atomic> #include <memory> +#include <optional> #include <ui/DisplayId.h> #include <ui/Fence.h> #include <ui/FenceTime.h> +#include <scheduler/Features.h> #include <scheduler/Time.h> #include <scheduler/VsyncId.h> #include <scheduler/interface/CompositeResult.h> @@ -49,14 +51,11 @@ public: TimePoint expectedPresentTime() const { return mExpectedPresentTime; } + std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; } + // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details. TimePoint pastVsyncTime(Period minFramePeriod) const; - // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead. - TimePoint previousFrameVsyncTime(Period minFramePeriod) const { - return mExpectedPresentTime - minFramePeriod; - } - // The present fence for the frame that had targeted the most recent VSYNC before this frame. // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the @@ -69,8 +68,6 @@ public: return mPresentFences.front().fenceTime; } - bool wouldPresentEarly(Period minFramePeriod) const; - bool isFramePending() const { return mFramePending; } bool didMissFrame() const { return mFrameMissed; } bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; } @@ -79,9 +76,17 @@ protected: explicit FrameTarget(const std::string& displayLabel); ~FrameTarget() = default; + bool wouldPresentEarly(Period minFramePeriod) const; + + // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead. + TimePoint previousFrameVsyncTime(Period minFramePeriod) const { + return mExpectedPresentTime - minFramePeriod; + } + VsyncId mVsyncId; TimePoint mFrameBeginTime; TimePoint mExpectedPresentTime; + std::optional<TimePoint> mEarliestPresentTime; TracedOrdinal<bool> mFramePending; TracedOrdinal<bool> mFrameMissed; @@ -95,6 +100,8 @@ protected: std::array<FenceWithFenceTime, 2> mPresentFences; private: + friend class FrameTargeterTestBase; + template <int N> inline bool targetsVsyncsAhead(Period minFramePeriod) const { static_assert(N > 1); @@ -105,9 +112,10 @@ private: // Computes a display's per-frame metrics about past/upcoming targeting of present deadlines. class FrameTargeter final : private FrameTarget { public: - FrameTargeter(PhysicalDisplayId displayId, bool backpressureGpuComposition) + FrameTargeter(PhysicalDisplayId displayId, FeatureFlags flags) : FrameTarget(to_string(displayId)), - mBackpressureGpuComposition(backpressureGpuComposition) {} + mBackpressureGpuComposition(flags.test(Feature::kBackpressureGpuComposition)), + mSupportsExpectedPresentTime(flags.test(Feature::kExpectedPresentTime)) {} const FrameTarget& target() const { return *this; } @@ -116,10 +124,14 @@ public: VsyncId vsyncId; TimePoint expectedVsyncTime; Duration sfWorkDuration; + Duration hwcMinWorkDuration; }; void beginFrame(const BeginFrameArgs&, const IVsyncSource&); + std::optional<TimePoint> computeEarliestPresentTime(Period minFramePeriod, + Duration hwcMinWorkDuration); + // TODO(b/241285191): Merge with FrameTargeter::endFrame. FenceTimePtr setPresentFence(sp<Fence>); @@ -128,7 +140,7 @@ public: void dump(utils::Dumper&) const; private: - friend class FrameTargeterTest; + friend class FrameTargeterTestBase; // For tests. using IsFencePendingFuncPtr = bool (*)(const FenceTimePtr&, int graceTimeMs); @@ -138,6 +150,7 @@ private: static bool isFencePending(const FenceTimePtr&, int graceTimeMs); const bool mBackpressureGpuComposition; + const bool mSupportsExpectedPresentTime; TimePoint mScheduledPresentTime; CompositionCoverageFlags mCompositionCoverage; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h index 0154060f36..f0f7a87105 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h @@ -16,13 +16,14 @@ #pragma once +#include <ftl/optional.h> #include <scheduler/Time.h> namespace android::scheduler { struct IVsyncSource { virtual Period period() const = 0; - virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0; + virtual TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const = 0; virtual Period minFramePeriod() const = 0; protected: diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h index 12ee36e084..8673a2251c 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h @@ -47,6 +47,9 @@ struct ICompositor { virtual CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&) = 0; + // Sends a hint about the expected present time + virtual void sendNotifyExpectedPresentHint(PhysicalDisplayId) = 0; + // Samples the composited frame via RegionSamplingThread. virtual void sample() = 0; diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp index e80372be31..68c277d499 100644 --- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp +++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp @@ -82,6 +82,10 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v } } + if (!mSupportsExpectedPresentTime) { + mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration); + } + ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId), ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)"); @@ -121,6 +125,14 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v if (mGpuFrameMissed) mGpuFrameMissedCount++; } +std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod, + Duration hwcMinWorkDuration) { + if (wouldPresentEarly(minFramePeriod)) { + return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration; + } + return {}; +} + void FrameTargeter::endFrame(const CompositeResult& result) { mCompositionCoverage = result.compositionCoverage; } diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp index 09e8a1ebe0..eeb9c60d15 100644 --- a/services/surfaceflinger/Scheduler/src/Timer.cpp +++ b/services/surfaceflinger/Scheduler/src/Timer.cpp @@ -159,7 +159,7 @@ bool Timer::dispatch() { ALOGW("Failed to set SCHED_FIFO on dispatch thread"); } - if (pthread_setname_np(pthread_self(), "TimerDispatch")) { + if (pthread_setname_np(pthread_self(), "TimerDispatch") != 0) { ALOGW("Failed to set thread name on dispatch thread"); } diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp index c883385755..29711afdf9 100644 --- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp +++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp @@ -38,18 +38,26 @@ struct VsyncSource final : IVsyncSource { const TimePoint vsyncDeadline; Period period() const override { return vsyncPeriod; } - TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; } + TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const override { + return vsyncDeadline; + } Period minFramePeriod() const override { return framePeriod; } }; } // namespace -class FrameTargeterTest : public testing::Test { +class FrameTargeterTestBase : public testing::Test { public: + FrameTargeterTestBase(FeatureFlags flags) : mTargeter(PhysicalDisplayId::fromPort(13), flags) {} + const auto& target() const { return mTargeter.target(); } + bool wouldPresentEarly(Period minFramePeriod) const { + return target().wouldPresentEarly(minFramePeriod); + } + struct Frame { - Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime, + Frame(FrameTargeterTestBase* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime, Duration frameDuration, Fps refreshRate, Fps peakRefreshRate, FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled, const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt) @@ -61,7 +69,8 @@ public: .vsyncId = vsyncId, .expectedVsyncTime = frameBeginTime + frameDuration, - .sfWorkDuration = 10ms}; + .sfWorkDuration = 10ms, + .hwcMinWorkDuration = kHwcMinWorkDuration}; testPtr->mTargeter.beginFrame(args, vsyncSourceOpt @@ -93,7 +102,7 @@ public: static bool fencePending(const FenceTimePtr&, int) { return true; } static bool fenceSignaled(const FenceTimePtr&, int) { return false; } - FrameTargeterTest* const testPtr; + FrameTargeterTestBase* const testPtr; TimePoint& frameBeginTime; const Period period; @@ -102,11 +111,24 @@ public: bool ended = false; }; + static constexpr Duration kHwcMinWorkDuration = std::chrono::nanoseconds(5ns); + private: FenceToFenceTimeMap mFenceMap; - static constexpr bool kBackpressureGpuComposition = true; - FrameTargeter mTargeter{PhysicalDisplayId::fromPort(13), kBackpressureGpuComposition}; + FrameTargeter mTargeter; +}; + +class FrameTargeterTest : public FrameTargeterTestBase { +public: + FrameTargeterTest() : FrameTargeterTestBase(Feature::kBackpressureGpuComposition) {} +}; + +class FrameTargeterWithExpectedPresentSupportTest : public FrameTargeterTestBase { +public: + FrameTargeterWithExpectedPresentSupportTest() + : FrameTargeterTestBase(FeatureFlags(Feature::kBackpressureGpuComposition) | + Feature::kExpectedPresentTime) {} }; TEST_F(FrameTargeterTest, targetsFrames) { @@ -208,7 +230,7 @@ TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) { TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) { constexpr Period kPeriod = (60_Hz).getPeriod(); EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE); - EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); } TEST_F(FrameTargeterTest, detectsEarlyPresent) { @@ -220,7 +242,8 @@ TEST_F(FrameTargeterTest, detectsEarlyPresent) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); } // The target is early if the past present fence was signaled. @@ -228,7 +251,41 @@ TEST_F(FrameTargeterTest, detectsEarlyPresent) { const auto fence = frame.end(); fence->signalForTest(frameBeginTime.ns()); - EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); + Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + + // `finalFrame` would present early, so it has an earliest present time. + EXPECT_TRUE(wouldPresentEarly(kPeriod)); + ASSERT_NE(std::nullopt, target().earliestPresentTime()); + EXPECT_EQ(*target().earliestPresentTime(), + target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); +} + +// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time +// when there is expected present time support. +TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) { + VsyncId vsyncId{333}; + TimePoint frameBeginTime(3000ms); + constexpr Fps kRefreshRate = 60_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + + // The target is not early while past present fences are pending. + for (int n = 3; n-- > 0;) { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); + } + + // The target is early if the past present fence was signaled. + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + const auto fence = frame.end(); + fence->signalForTest(frameBeginTime.ns()); + + Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + + // `finalFrame` would present early, but we have expected present time support, so it has no + // earliest present time. + EXPECT_TRUE(wouldPresentEarly(kPeriod)); + ASSERT_EQ(std::nullopt, target().earliestPresentTime()); } TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { @@ -240,7 +297,8 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); } Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); @@ -248,12 +306,18 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { fence->signalForTest(frameBeginTime.ns()); // The target is two VSYNCs ahead, so the past present fence is still pending. - EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); } + Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + // The target is early if the past present fence was signaled. - EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); + EXPECT_TRUE(wouldPresentEarly(kPeriod)); + ASSERT_NE(std::nullopt, target().earliestPresentTime()); + EXPECT_EQ(*target().earliestPresentTime(), + target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); } TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) { @@ -264,7 +328,10 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) { const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate); // The target is more than two VSYNCs ahead, but present fences are not tracked that far back. - EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); + EXPECT_TRUE(wouldPresentEarly(kPeriod)); + EXPECT_TRUE(target().earliestPresentTime()); + EXPECT_EQ(*target().earliestPresentTime(), + target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); } TEST_F(FrameTargeterTest, detectsMissedFrames) { diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ab0598d90b..0f8e3bfb86 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -117,6 +117,7 @@ #include <common/FlagManager.h> #include <gui/LayerStatePermissions.h> #include <gui/SchedulingPolicy.h> +#include <gui/SyncScreenCaptureListener.h> #include <ui/DisplayIdentification.h> #include "BackgroundExecutor.h" #include "Client.h" @@ -233,31 +234,25 @@ bool validateCompositionDataspace(Dataspace dataspace) { } std::chrono::milliseconds getIdleTimerTimeout(DisplayId displayId) { - const auto displayIdleTimerMsKey = [displayId] { - std::stringstream ss; - ss << "debug.sf.set_idle_timer_ms_" << displayId.value; - return ss.str(); - }(); - - const int32_t displayIdleTimerMs = base::GetIntProperty(displayIdleTimerMsKey, 0); - if (displayIdleTimerMs > 0) { + if (const int32_t displayIdleTimerMs = + base::GetIntProperty("debug.sf.set_idle_timer_ms_"s + + std::to_string(displayId.value), + 0); + displayIdleTimerMs > 0) { return std::chrono::milliseconds(displayIdleTimerMs); } - const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms", 0); + const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0); const int32_t millis = setIdleTimerMs ? setIdleTimerMs : sysprop::set_idle_timer_ms(0); return std::chrono::milliseconds(millis); } bool getKernelIdleTimerSyspropConfig(DisplayId displayId) { - const auto displaySupportKernelIdleTimerKey = [displayId] { - std::stringstream ss; - ss << "debug.sf.support_kernel_idle_timer_" << displayId.value; - return ss.str(); - }(); + const bool displaySupportKernelIdleTimer = + base::GetBoolProperty("debug.sf.support_kernel_idle_timer_"s + + std::to_string(displayId.value), + false); - const auto displaySupportKernelIdleTimer = - base::GetBoolProperty(displaySupportKernelIdleTimerKey, false); return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false); } @@ -467,11 +462,10 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI wideColorGamutCompositionPixelFormat = static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888)); - mLayerCachingEnabled = [] { - const bool enable = - android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false); - return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable); - }(); + mLayerCachingEnabled = + base::GetBoolProperty("debug.sf.enable_layer_caching"s, + sysprop::SurfaceFlingerProperties::enable_layer_caching() + .value_or(false)); useContextPriority = use_context_priority(true); @@ -493,14 +487,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mSupportsBlur = supportsBlurs; ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported."); - const size_t defaultListSize = MAX_LAYERS; - auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize)); - mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize; - mGraphicBufferProducerListSizeLogThreshold = - std::max(static_cast<int>(0.95 * - static_cast<double>(mMaxGraphicBufferProducerListSize)), - 1); - property_get("debug.sf.luma_sampling", value, "1"); mLumaSampling = atoi(value); @@ -552,6 +538,12 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true); mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled || base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false); + + // These are set by the HWC implementation to indicate that they will use the workarounds. + mIsHotplugErrViaNegVsync = + base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false); + + mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false); } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { @@ -764,7 +756,7 @@ void SurfaceFlinger::bootFinished() { sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { if (input == nullptr) { ALOGE("Failed to link to input service"); } else { @@ -801,22 +793,27 @@ void SurfaceFlinger::bootFinished() { })); } -static std::optional<renderengine::RenderEngine::RenderEngineType> -chooseRenderEngineTypeViaSysProp() { +void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) { char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "skiaglthreaded"); + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); if (strcmp(prop, "skiagl") == 0) { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL; + builder.setThreaded(renderengine::RenderEngine::Threaded::NO) + .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL); } else if (strcmp(prop, "skiaglthreaded") == 0) { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED; + builder.setThreaded(renderengine::RenderEngine::Threaded::YES) + .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL); } else if (strcmp(prop, "skiavk") == 0) { - return renderengine::RenderEngine::RenderEngineType::SKIA_VK; + builder.setThreaded(renderengine::RenderEngine::Threaded::NO) + .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK); } else if (strcmp(prop, "skiavkthreaded") == 0) { - return renderengine::RenderEngine::RenderEngineType::SKIA_VK_THREADED; + builder.setThreaded(renderengine::RenderEngine::Threaded::YES) + .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK); } else { - ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop); - return {}; + const auto kVulkan = renderengine::RenderEngine::GraphicsApi::VK; + const bool useVulkan = FlagManager::getInstance().vulkan_renderengine() && + renderengine::RenderEngine::canSupport(kVulkan); + builder.setGraphicsApi(useVulkan ? kVulkan : renderengine::RenderEngine::GraphicsApi::GL); } } @@ -842,9 +839,7 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { useContextPriority ? renderengine::RenderEngine::ContextPriority::REALTIME : renderengine::RenderEngine::ContextPriority::MEDIUM); - if (auto type = chooseRenderEngineTypeViaSysProp()) { - builder.setRenderEngineType(type.value()); - } + chooseRenderEngineType(builder); mRenderEngine = renderengine::RenderEngine::create(builder.build()); mCompositionEngine->setRenderEngine(mRenderEngine.get()); mMaxRenderTargetSize = @@ -892,7 +887,6 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { // the DisplayDevice, hence the above commit of the primary display. Remove that special case by // initializing the Scheduler after configureLocked, once decoupled from DisplayDevice. initScheduler(display); - dispatchDisplayHotplugEvent(display->getPhysicalId(), true); mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) { auto snapshot = perfetto::protos::LayersSnapshotProto{}; @@ -1083,7 +1077,7 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info for (const auto& [id, mode] : displayModes) { ui::DisplayMode outMode; - outMode.id = static_cast<int32_t>(id.value()); + outMode.id = ftl::to_underlying(id); auto [width, height] = mode->getResolution(); auto [xDpi, yDpi] = mode->getDpi(); @@ -1103,7 +1097,7 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info outMode.peakRefreshRate = peakFps.getValue(); outMode.vsyncRate = mode->getVsyncRate().getValue(); - const auto vsyncConfigSet = mVsyncConfiguration->getConfigsForRefreshRate( + const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate( Fps::fromValue(outMode.peakRefreshRate)); outMode.appVsyncOffset = vsyncConfigSet.late.appOffset; outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset; @@ -1132,7 +1126,7 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info const PhysicalDisplayId displayId = snapshot.displayId(); const auto mode = display->refreshRateSelector().getActiveMode(); - info->activeDisplayModeId = mode.modePtr->getId().value(); + info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId()); info->renderFrameRate = mode.fps.getValue(); info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities()); @@ -1148,7 +1142,7 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) { if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) { if (const auto modeId = snapshot.translateModeId(*hwcId)) { - info->preferredBootDisplayMode = modeId->value(); + info->preferredBootDisplayMode = ftl::to_underlying(*modeId); } } } @@ -1230,8 +1224,10 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken, return NO_ERROR; } -void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) { - const auto displayId = request.mode.modePtr->getPhysicalDisplayId(); +void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { + const auto mode = desiredMode.mode; + const auto displayId = mode.modePtr->getPhysicalDisplayId(); + ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); const auto display = getDisplayDeviceLocked(displayId); @@ -1240,10 +1236,9 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool return; } - const auto mode = request.mode; - const bool emitEvent = request.emitEvent; + const bool emitEvent = desiredMode.emitEvent; - switch (display->setDesiredMode(std::move(request), force)) { + switch (display->setDesiredMode(std::move(desiredMode))) { case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch: // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler. mScheduler->setRenderRate(displayId, @@ -1262,7 +1257,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated); if (displayId == mActiveDisplayId) { - updatePhaseConfiguration(mode.fps); + mScheduler->updatePhaseConfiguration(mode.fps); } mScheduler->setModeChangePending(true); @@ -1271,8 +1266,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool mScheduler->setRenderRate(displayId, mode.fps); if (displayId == mActiveDisplayId) { - updatePhaseConfiguration(mode.fps); - mRefreshRateStats->setRefreshRate(mode.fps); + mScheduler->updatePhaseConfiguration(mode.fps); } if (emitEvent) { @@ -1293,7 +1287,7 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke } const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { const auto displayOpt = FTL_FAKE_GUARD(mStateLock, ftl::find_if(mPhysicalDisplays, @@ -1312,7 +1306,7 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke [](const DisplayModePtr& mode) { return mode->getPeakFps(); }); if (!fpsOpt) { - ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(), + ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId), to_string(snapshot.displayId()).c_str()); return BAD_VALUE; } @@ -1364,8 +1358,7 @@ void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) { activeMode.fps); if (displayId == mActiveDisplayId) { - mRefreshRateStats->setRefreshRate(activeMode.fps); - updatePhaseConfiguration(activeMode.fps); + mScheduler->updatePhaseConfiguration(activeMode.fps); } if (pendingModeOpt->emitEvent) { @@ -1394,7 +1387,7 @@ void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) { mScheduler->setRenderRate(displayId, renderFps); if (displayId == mActiveDisplayId) { - updatePhaseConfiguration(renderFps); + mScheduler->updatePhaseConfiguration(renderFps); } } @@ -1422,16 +1415,17 @@ void SurfaceFlinger::initiateDisplayModeChanges() { if (!displayModePtrOpt) { ALOGW("Desired display mode is no longer supported. Mode ID = %d", - desiredModeId.value()); - dropModeRequest(display); + ftl::to_underlying(desiredModeId)); continue; } - ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(), + ALOGV("%s changing active mode to %d(%s) for display %s", __func__, + ftl::to_underlying(desiredModeId), to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(), to_string(display->getId()).c_str()); - if (display->getActiveMode() == desiredModeOpt->mode) { + if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) && + display->getActiveMode() == desiredModeOpt->mode) { applyActiveMode(display); continue; } @@ -1480,7 +1474,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { void SurfaceFlinger::disableExpensiveRendering() { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { ATRACE_NAME(whence); if (mPowerAdvisor->isUsingExpensiveRendering()) { for (const auto& [_, display] : mDisplays) { @@ -1522,7 +1516,7 @@ status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui: } const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) .transform(&ftl::to_mapped_ref<PhysicalDisplays>) @@ -1603,7 +1597,7 @@ status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken, DisplayModeId modeId) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { const auto snapshotOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) .transform(&ftl::to_mapped_ref<PhysicalDisplays>) @@ -1619,7 +1613,7 @@ status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& dis [](const DisplayModePtr& mode) { return mode->getHwcId(); }); if (!hwcIdOpt) { - ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(), + ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId), to_string(snapshot.displayId()).c_str()); return BAD_VALUE; } @@ -1631,7 +1625,7 @@ status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& dis status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { return getHwComposer().clearBootDisplayMode(*displayId); } else { @@ -1670,7 +1664,7 @@ status_t SurfaceFlinger::setHdrConversionStrategy( ALOGE("hdrOutputConversion is not supported by this device."); return INVALID_OPERATION; } - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t { using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy; using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag; @@ -1730,7 +1724,7 @@ status_t SurfaceFlinger::getHdrOutputConversionSupport(bool* outSupport) const { void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) { const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { getHwComposer().setAutoLowLatencyMode(*displayId, on); } else { @@ -1741,7 +1735,7 @@ void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) { const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE; getHwComposer().setContentType(*displayId, type); @@ -1762,7 +1756,7 @@ status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken, } display->overrideHdrTypes(hdrTypes); - dispatchDisplayHotplugEvent(display->getPhysicalId(), true /* connected */); + mScheduler->dispatchHotplug(display->getPhysicalId(), scheduler::Scheduler::Hotplug::Connected); return NO_ERROR; } @@ -1795,7 +1789,7 @@ status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& dis bool enable, uint8_t componentMask, uint64_t maxFrames) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable, componentMask, maxFrames); @@ -1848,7 +1842,7 @@ status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken, status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) { outLayers->clear(); - auto future = mScheduler->schedule([=] { + auto future = mScheduler->schedule([=, this] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); mDrawingState.traverseInZOrder([&](Layer* layer) { outLayers->push_back(layer->getLayerDebugInfo(display.get())); @@ -1954,7 +1948,8 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, } const char* const whence = __func__; - return ftl::Future(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { + // TODO(b/241285876): Validate that the display is physical instead of failing later. if (const auto display = getDisplayDeviceLocked(displayToken)) { const bool supportsDisplayBrightnessCommand = getHwComposer().getComposer()->isSupported( @@ -2004,7 +1999,6 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, Hwc2::Composer::DisplayBrightnessOptions{ .applyImmediately = true}); } - } else { ALOGE("%s: Invalid display token %p", whence, displayToken.get()); return ftl::yield<status_t>(NAME_NOT_FOUND); @@ -2091,12 +2085,11 @@ status_t SurfaceFlinger::getDisplayDecorationSupport( sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration, const sp<IBinder>& layerHandle) { - const auto& handle = - vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger - ? mSfConnectionHandle - : mAppConnectionHandle; + const auto cycle = vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger + ? scheduler::Cycle::LastComposite + : scheduler::Cycle::Render; - return mScheduler->createDisplayEventConnection(handle, eventRegistration, layerHandle); + return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle); } void SurfaceFlinger::scheduleCommit(FrameHint hint) { @@ -2131,13 +2124,24 @@ nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { - if (FlagManager::getInstance().connected_display()) { + if (FlagManager::getInstance().connected_display() && timestamp < 0 && + vsyncPeriod.has_value()) { // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32 - if (mIsHotplugErrViaNegVsync && timestamp < 0 && vsyncPeriod.has_value() && - vsyncPeriod.value() == ~0) { - int hotplugErrorCode = static_cast<int32_t>(-timestamp); - ALOGD("SurfaceFlinger got hotplugErrorCode=%d", hotplugErrorCode); - mScheduler->onHotplugConnectionError(mAppConnectionHandle, hotplugErrorCode); + if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) { + const auto errorCode = static_cast<int32_t>(-timestamp); + ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId); + mScheduler->dispatchHotplugError(errorCode); + return; + } + + if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) { + const int32_t value = static_cast<int32_t>(-timestamp); + // one byte is good enough to encode android.hardware.drm.HdcpLevel + const int32_t maxLevel = (value >> 8) & 0xFF; + const int32_t connectedLevel = value & 0xFF; + ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for display %" PRIu64, __func__, + connectedLevel, maxLevel, hwcDisplayId); + updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel); return; } } @@ -2174,9 +2178,10 @@ void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, } if (FlagManager::getInstance().hotplug2()) { - ALOGD("SurfaceFlinger got hotplug event=%d", static_cast<int32_t>(event)); // TODO(b/311403559): use enum type instead of int - mScheduler->onHotplugConnectionError(mAppConnectionHandle, static_cast<int32_t>(event)); + const auto errorCode = static_cast<int32_t>(event); + ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId); + mScheduler->dispatchHotplugError(errorCode); } } @@ -2209,7 +2214,7 @@ void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData ATRACE_CALL(); if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) { const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { const Fps fps = Fps::fromPeriodNsecs(getHwComposer().getComposer()->isVrrSupported() ? data.refreshPeriodNanos : data.vsyncPeriodNanos); @@ -2373,11 +2378,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, mLegacyLayers[layer->sequence] = layer; } } - if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) { - ATRACE_NAME("LayerHierarchyBuilder:update"); - mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(), - mLayerLifecycleManager.getDestroyedLayers()); - } + mLayerHierarchyBuilder.update(mLayerLifecycleManager); } // Keep a copy of the drawing state (that is going to be overwritten @@ -2443,6 +2444,13 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch(); auto it = mLegacyLayers.find(layer->id); + if (it == mLegacyLayers.end() && + layer->changes.test(frontend::RequestedLayerState::Changes::Destroyed)) { + // Layer handle was created and immediately destroyed. It was destroyed before it + // was added to the map. + continue; + } + LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s", layer->getDebugString().c_str()); @@ -2675,6 +2683,8 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( if (const auto display = getCompositionDisplayLocked(id)) { refreshArgs.outputs.push_back(display); } + + refreshArgs.frameTargets.try_emplace(id, &targeter->target()); } std::vector<DisplayId> displayIds; @@ -2743,36 +2753,15 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay); } - const Period minFramePeriod = mScheduler->getVsyncSchedule()->minFramePeriod(); - - if (!getHwComposer().getComposer()->isSupported( - Hwc2::Composer::OptionalFeature::ExpectedPresentTime) && - pacesetterTarget.wouldPresentEarly(minFramePeriod)) { - const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration; - - // TODO(b/255601557): Calculate and pass per-display values for each FrameTarget. - refreshArgs.earliestPresentTime = - pacesetterTarget.previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration; - } - - const TimePoint expectedPresentTime = pacesetterTarget.expectedPresentTime(); // TODO(b/255601557) Update frameInterval per display - refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, expectedPresentTime); - refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); - refreshArgs.expectedPresentTime = expectedPresentTime.ns(); + refreshArgs.frameInterval = + mScheduler->getNextFrameInterval(pacesetterId, pacesetterTarget.expectedPresentTime()); + const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult(); + const auto scheduledFrameTimeOpt = scheduledFrameResultOpt + ? std::optional{scheduledFrameResultOpt->callbackTime} + : std::nullopt; + refreshArgs.scheduledFrameTime = scheduledFrameTimeOpt; refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0; - { - auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId]; - auto lastExpectedPresentTimestamp = TimePoint::fromNs( - notifyExpectedPresentData.lastExpectedPresentTimestamp.load().ns()); - if (expectedPresentTime > lastExpectedPresentTimestamp) { - // If the values are not same, then hint is sent with newer value. - // And because composition always follows the notifyExpectedPresentIfRequired, we can - // skip updating the lastExpectedPresentTimestamp in this case. - notifyExpectedPresentData.lastExpectedPresentTimestamp - .compare_exchange_weak(lastExpectedPresentTimestamp, expectedPresentTime); - } - } // Store the present time just before calling to the composition engine so we could notify // the scheduler. const auto presentTime = systemTime(); @@ -2836,6 +2825,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( scheduleComposite(FrameHint::kNone); } + mNotifyExpectedPresentMap[pacesetterId].hintStatus = NotifyExpectedPresentHintStatus::Start; onCompositionPresented(pacesetterId, frameTargeters, presentTime); const bool hadGpuComposited = @@ -3056,7 +3046,8 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const auto schedule = mScheduler->getVsyncSchedule(); const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime); const Period vsyncPeriod = schedule->period(); - const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset; + const nsecs_t vsyncPhase = + mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset; const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase, presentLatency.ns()); @@ -3100,7 +3091,7 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, { Mutex::Autolock lock(mStateLock); if (mFpsReporter) { - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy()); } if (mTunnelModeEnabledReporter) { @@ -3122,6 +3113,7 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, for (auto& [compositionDisplay, listener] : hdrInfoListeners) { HdrLayerInfoReporter::HdrLayerInfo info; int32_t maxArea = 0; + auto updateInfoFn = [&](const std::shared_ptr<compositionengine::Display>& compositionDisplay, const frontend::LayerSnapshot& snapshot, const sp<LayerFE>& layerFe) { @@ -3132,7 +3124,7 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, compositionDisplay->getOutputLayerForLayer(layerFe); if (outputLayer) { const float desiredHdrSdrRatio = - snapshot.desiredHdrSdrRatio <= 1.f + snapshot.desiredHdrSdrRatio < 1.f ? std::numeric_limits<float>::infinity() : snapshot.desiredHdrSdrRatio; info.mergeDesiredRatio(desiredHdrSdrRatio); @@ -3308,7 +3300,11 @@ void SurfaceFlinger::commitTransactionsLegacy() { std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( PhysicalDisplayId displayId) const { std::vector<HWComposer::HWCDisplayMode> hwcModes; - std::optional<hal::HWDisplayId> activeModeHwcId; + std::optional<hal::HWConfigId> activeModeHwcIdOpt; + + const bool isExternalDisplay = FlagManager::getInstance().connected_display() && + getHwComposer().getDisplayConnectionType(displayId) == + ui::DisplayConnectionType::External; int attempt = 0; constexpr int kMaxAttempts = 3; @@ -3316,10 +3312,81 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( hwcModes = getHwComposer().getModes(displayId, scheduler::RefreshRateSelector::kMinSupportedFrameRate .getPeriodNsecs()); - activeModeHwcId = getHwComposer().getActiveMode(displayId); + const auto activeModeHwcIdExp = getHwComposer().getActiveMode(displayId); + activeModeHwcIdOpt = activeModeHwcIdExp.value_opt(); + + if (isExternalDisplay && + activeModeHwcIdExp.has_error([](status_t error) { return error == NO_INIT; })) { + constexpr nsecs_t k59HzVsyncPeriod = 16949153; + constexpr nsecs_t k60HzVsyncPeriod = 16666667; + + // DM sets the initial mode for an external display to 1080p@60, but + // this comes after SF creates its own state (including the + // DisplayDevice). For now, pick the same mode in order to avoid + // inconsistent state and unnecessary mode switching. + // TODO (b/318534874): Let DM decide the initial mode. + // + // Try to find 1920x1080 @ 60 Hz + if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(), + [](const auto& mode) { + return mode.width == 1920 && + mode.height == 1080 && + mode.vsyncPeriod == k60HzVsyncPeriod; + }); + iter != hwcModes.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } + + // Try to find 1920x1080 @ 59-60 Hz + if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(), + [](const auto& mode) { + return mode.width == 1920 && + mode.height == 1080 && + mode.vsyncPeriod >= k60HzVsyncPeriod && + mode.vsyncPeriod <= k59HzVsyncPeriod; + }); + iter != hwcModes.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } - const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) { - return mode.hwcId == activeModeHwcId; + // The display does not support 1080p@60, and this is the last attempt to pick a display + // mode. Prefer 60 Hz if available, with the closest resolution to 1080p. + if (attempt + 1 == kMaxAttempts) { + std::vector<HWComposer::HWCDisplayMode> hwcModeOpts; + + for (const auto& mode : hwcModes) { + if (mode.width <= 1920 && mode.height <= 1080 && + mode.vsyncPeriod >= k60HzVsyncPeriod && + mode.vsyncPeriod <= k59HzVsyncPeriod) { + hwcModeOpts.push_back(mode); + } + } + + if (const auto iter = std::max_element(hwcModeOpts.begin(), hwcModeOpts.end(), + [](const auto& a, const auto& b) { + const auto aSize = a.width * a.height; + const auto bSize = b.width * b.height; + if (aSize < bSize) + return true; + else if (aSize == bSize) + return a.vsyncPeriod > b.vsyncPeriod; + else + return false; + }); + iter != hwcModeOpts.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } + + // hwcModeOpts was empty, use hwcModes[0] as the last resort + activeModeHwcIdOpt = hwcModes[0].hwcId; + } + } + + const auto isActiveMode = [activeModeHwcIdOpt](const HWComposer::HWCDisplayMode& mode) { + return mode.hwcId == activeModeHwcIdOpt; }; if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) { @@ -3329,7 +3396,7 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( if (attempt == kMaxAttempts) { const std::string activeMode = - activeModeHwcId ? std::to_string(*activeModeHwcId) : "unknown"s; + activeModeHwcIdOpt ? std::to_string(*activeModeHwcIdOpt) : "unknown"s; ALOGE("HWC failed to report an active mode that is supported: activeModeHwcId=%s, " "hwcModes={%s}", activeMode.c_str(), base::Join(hwcModes, ", ").c_str()); @@ -3342,15 +3409,15 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( }) .value_or(DisplayModes{}); - ui::DisplayModeId nextModeId = 1 + - std::accumulate(oldModes.begin(), oldModes.end(), static_cast<ui::DisplayModeId>(-1), - [](ui::DisplayModeId max, const auto& pair) { - return std::max(max, pair.first.value()); - }); + DisplayModeId nextModeId = std::accumulate(oldModes.begin(), oldModes.end(), DisplayModeId(-1), + [](DisplayModeId max, const auto& pair) { + return std::max(max, pair.first); + }); + ++nextModeId; DisplayModes newModes; for (const auto& hwcMode : hwcModes) { - const DisplayModeId id{nextModeId++}; + const auto id = nextModeId++; newModes.try_emplace(id, DisplayMode::Builder(hwcMode.hwcId) .setId(id) @@ -3373,10 +3440,14 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( // Keep IDs if modes have not changed. const auto& modes = sameModes ? oldModes : newModes; const DisplayModePtr activeMode = - std::find_if(modes.begin(), modes.end(), [activeModeHwcId](const auto& pair) { - return pair.second->getHwcId() == activeModeHwcId; + std::find_if(modes.begin(), modes.end(), [activeModeHwcIdOpt](const auto& pair) { + return pair.second->getHwcId() == activeModeHwcIdOpt; })->second; + if (isExternalDisplay) { + ALOGI("External display %s initial mode: {%s}", to_string(displayId).c_str(), + to_string(*activeMode).c_str()); + } return {modes, activeMode}; } @@ -3421,8 +3492,11 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, auto [displayModes, activeMode] = loadDisplayModes(displayId); if (!activeMode) { - // TODO(b/241286153): Report hotplug failure to the framework. ALOGE("Failed to hotplug display %s", to_string(displayId).c_str()); + if (FlagManager::getInstance().hotplug2()) { + mScheduler->dispatchHotplugError( + static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN)); + } getHwComposer().disconnectDisplay(displayId); return nullptr; } @@ -3452,9 +3526,10 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, } const sp<IBinder> token = sp<BBinder>::make(); + const ui::DisplayConnectionType connectionType = + getHwComposer().getDisplayConnectionType(displayId); - mPhysicalDisplays.try_emplace(displayId, token, displayId, - getHwComposer().getDisplayConnectionType(displayId), + mPhysicalDisplays.try_emplace(displayId, token, displayId, connectionType, std::move(displayModes), std::move(colorModes), std::move(info.deviceProductInfo)); @@ -3462,7 +3537,7 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, state.physical = {.id = displayId, .hwcDisplayId = hwcDisplayId, .activeMode = std::move(activeMode)}; - state.isSecure = true; // All physical displays are currently considered secure. + state.isSecure = connectionType == ui::DisplayConnectionType::Internal; state.isProtected = true; state.displayName = std::move(info.name); @@ -3470,11 +3545,6 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, return "Connecting"; } -void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) { - mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected); - mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected); -} - void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId, const scheduler::FrameRateMode& mode) { // TODO(b/255635821): Merge code paths and move to Scheduler. @@ -3482,7 +3552,7 @@ void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId, ? &scheduler::Scheduler::onPrimaryDisplayModeChanged : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged; - ((*mScheduler).*onDisplayModeChanged)(mAppConnectionHandle, mode); + ((*mScheduler).*onDisplayModeChanged)(scheduler::Cycle::Render, mode); } sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( @@ -3509,10 +3579,10 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( const auto enableFrameRateOverride = sysprop::enable_frame_rate_override(true) ? Config::FrameRateOverride::Enabled : Config::FrameRateOverride::Disabled; - Config config = + const Config config = {.enableFrameRateOverride = enableFrameRateOverride, .frameRateMultipleThreshold = - base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0), + base::GetIntProperty("debug.sf.frame_rate_multiple_threshold"s, 0), .idleTimerTimeout = idleTimerTimeoutMs, .kernelIdleTimerController = kernelIdleTimerController}; @@ -3561,9 +3631,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary); ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation)); - // virtual displays are always considered enabled - creationArgs.initialPowerMode = - state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt; + creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF; creationArgs.requestedRefreshRate = state.requestedRefreshRate; @@ -3665,16 +3733,11 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, displaySurface, producer); if (mScheduler && !display->isVirtual()) { - const auto displayId = display->getPhysicalId(); - { - // TODO(b/241285876): Annotate `processDisplayAdded` instead. - ftl::FakeGuard guard(kMainThreadContext); + // TODO(b/241285876): Annotate `processDisplayAdded` instead. + ftl::FakeGuard guard(kMainThreadContext); - // For hotplug reconnect, renew the registration since display modes have been reloaded. - mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector()); - } - - dispatchDisplayHotplugEvent(displayId, true); + // For hotplug reconnect, renew the registration since display modes have been reloaded. + mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); } if (display->isVirtual()) { @@ -3682,6 +3745,27 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, } mDisplays.try_emplace(displayToken, std::move(display)); + + // For an external display, loadDisplayModes already attempted to select the same mode + // as DM, but SF still needs to be updated to match. + // TODO (b/318534874): Let DM decide the initial mode. + if (const auto& physical = state.physical; + mScheduler && physical && FlagManager::getInstance().connected_display()) { + const bool isInternalDisplay = mPhysicalDisplays.get(physical->id) + .transform(&PhysicalDisplay::isInternal) + .value_or(false); + + if (!isInternalDisplay) { + auto activeModePtr = physical->activeMode; + const auto fps = activeModePtr->getPeakFps(); + + setDesiredMode( + {.mode = scheduler::FrameRateMode{fps, + ftl::as_non_null(std::move(activeModePtr))}, + .emitEvent = false, + .force = true}); + } + } } void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { @@ -3692,7 +3776,6 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { if (display->isVirtual()) { releaseVirtualDisplay(display->getVirtualId()); } else { - dispatchDisplayHotplugEvent(display->getPhysicalId(), false); mScheduler->unregisterDisplay(display->getPhysicalId()); } } @@ -3745,7 +3828,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, // TODO(b/175678251) Call a listener instead. if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) { - resetPhaseConfiguration(display->getActiveMode().fps); + mScheduler->resetPhaseConfiguration(display->getActiveMode().fps); } } return; @@ -3781,15 +3864,6 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, } } -void SurfaceFlinger::resetPhaseConfiguration(Fps refreshRate) { - // Cancel the pending refresh rate change, if any, before updating the phase configuration. - mScheduler->vsyncModulator().cancelRefreshRateChange(); - - mVsyncConfiguration->reset(); - updatePhaseConfiguration(refreshRate); - mRefreshRateStats->setRefreshRate(refreshRate); -} - void SurfaceFlinger::processDisplayChangesLocked() { // here we take advantage of Vector's copy-on-write semantics to // improve performance by skipping the transaction entirely when @@ -3800,6 +3874,9 @@ void SurfaceFlinger::processDisplayChangesLocked() { mVisibleRegionsDirty = true; mUpdateInputInfo = true; + // Apply the current color matrix to any added or changed display. + mCurrentState.colorMatrixChanged = true; + // find the displays that were removed // (ie: in drawing state but not in current state) // also handle displays that changed @@ -4109,8 +4186,8 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest if (display->refreshRateSelector().isModeAllowed(request.mode)) { setDesiredMode(std::move(request)); } else { - ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(), - to_string(displayId).c_str()); + ALOGV("%s: Mode %d is disallowed for display %s", __func__, + ftl::to_underlying(modePtr->getId()), to_string(displayId).c_str()); } } } @@ -4121,7 +4198,7 @@ void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { return getDefaultDisplayDeviceLocked()->getPhysicalId(); }(); - mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); + mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); } void SurfaceFlinger::notifyCpuLoadUp() { @@ -4136,8 +4213,9 @@ void SurfaceFlinger::onChoreographerAttached() { } } -void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime, - ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) { +void SurfaceFlinger::onExpectedPresentTimePosted(TimePoint expectedPresentTime, + ftl::NonNull<DisplayModePtr> modePtr, + Fps renderRate) { const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod(); const auto timeoutOpt = [&]() -> std::optional<Period> { const auto vrrConfig = modePtr->getVrrConfig(); @@ -4146,7 +4224,7 @@ void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime, const auto notifyExpectedPresentConfig = modePtr->getVrrConfig()->notifyExpectedPresentConfig; if (!notifyExpectedPresentConfig) return std::nullopt; - return Period::fromNs(notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs); + return Period::fromNs(notifyExpectedPresentConfig->timeoutNs); }(); notifyExpectedPresentIfRequired(modePtr->getPhysicalDisplayId(), vsyncPeriod, @@ -4158,45 +4236,122 @@ void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId TimePoint expectedPresentTime, Fps frameInterval, std::optional<Period> timeoutOpt) { - { - auto& data = mNotifyExpectedPresentMap[displayId]; - const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp.load(); - const auto lastFrameInterval = data.lastFrameInterval; - data.lastFrameInterval = frameInterval; - const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); - - const constexpr nsecs_t kOneSecondNs = - std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); - const auto timeout = Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns() - : kOneSecondNs); - const bool frameIntervalIsOnCadence = - isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, - lastFrameInterval, timeout, threshold); - - const bool expectedPresentWithinTimeout = - isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, - timeoutOpt, threshold); - - using fps_approx_ops::operator!=; - if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) { - data.lastExpectedPresentTimestamp = expectedPresentTime; - } - - if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { - return; + auto& data = mNotifyExpectedPresentMap[displayId]; + const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp; + const auto lastFrameInterval = data.lastFrameInterval; + data.lastFrameInterval = frameInterval; + data.lastExpectedPresentTimestamp = expectedPresentTime; + const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); + + const constexpr nsecs_t kOneSecondNs = + std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); + const auto timeout = + Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns() : kOneSecondNs); + const bool frameIntervalIsOnCadence = + isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, + lastFrameInterval, timeout, threshold); + + const bool expectedPresentWithinTimeout = + isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, + timeoutOpt, threshold); + if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + return; + } + + auto hintStatus = data.hintStatus.load(); + if (!expectedPresentWithinTimeout) { + if ((hintStatus != NotifyExpectedPresentHintStatus::Sent && + hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnTx) || + (timeoutOpt && timeoutOpt->ns() == 0)) { + // Send the hint immediately if timeout, as the hint gets + // delayed otherwise, as the frame is scheduled close + // to the actual present. + if (data.hintStatus + .compare_exchange_strong(hintStatus, + NotifyExpectedPresentHintStatus::ScheduleOnTx)) { + scheduleNotifyExpectedPresentHint(displayId); + return; + } } - data.lastExpectedPresentTimestamp = expectedPresentTime; + } + + if (hintStatus == NotifyExpectedPresentHintStatus::Sent && + data.hintStatus.compare_exchange_strong(hintStatus, + NotifyExpectedPresentHintStatus::ScheduleOnTx)) { + return; + } + if (hintStatus != NotifyExpectedPresentHintStatus::Start) { + return; + } + data.hintStatus.store(NotifyExpectedPresentHintStatus::ScheduleOnPresent); + mScheduler->scheduleFrame(); +} + +void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId, + VsyncId vsyncId) { + auto itr = mNotifyExpectedPresentMap.find(displayId); + if (itr == mNotifyExpectedPresentMap.end()) { + return; } const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { + const auto sendHint = [=, this]() { + auto& data = mNotifyExpectedPresentMap.at(displayId); + TimePoint expectedPresentTime = data.lastExpectedPresentTimestamp; + if (ftl::to_underlying(vsyncId) != FrameTimelineInfo::INVALID_VSYNC_ID) { + const auto predictionOpt = mFrameTimeline->getTokenManager()->getPredictionsForToken( + ftl::to_underlying(vsyncId)); + const auto expectedPresentTimeOnPredictor = TimePoint::fromNs( + predictionOpt ? predictionOpt->presentTime : expectedPresentTime.ns()); + const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult(); + const auto expectedPresentTimeOnScheduler = scheduledFrameResultOpt.has_value() + ? scheduledFrameResultOpt->vsyncTime + : TimePoint::fromNs(0); + expectedPresentTime = + std::max(expectedPresentTimeOnPredictor, expectedPresentTimeOnScheduler); + } + + if (expectedPresentTime < TimePoint::now()) { + expectedPresentTime = + mScheduler->getVsyncSchedule()->vsyncDeadlineAfter(TimePoint::now()); + if (mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration > + mScheduler->getVsyncSchedule(displayId)->period()) { + expectedPresentTime += mScheduler->getVsyncSchedule(displayId)->period(); + } + } const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime, - frameInterval); + data.lastFrameInterval); if (status != NO_ERROR) { ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence, displayId.value); } - })); + }; + + if (itr->second.hintStatus == NotifyExpectedPresentHintStatus::ScheduleOnTx) { + return static_cast<void>(mScheduler->schedule([=, + this]() FTL_FAKE_GUARD(kMainThreadContext) { + auto& data = mNotifyExpectedPresentMap.at(displayId); + auto scheduleHintOnTx = NotifyExpectedPresentHintStatus::ScheduleOnTx; + if (data.hintStatus.compare_exchange_strong(scheduleHintOnTx, + NotifyExpectedPresentHintStatus::Sent)) { + sendHint(); + } + })); + } + auto scheduleHintOnPresent = NotifyExpectedPresentHintStatus::ScheduleOnPresent; + if (itr->second.hintStatus.compare_exchange_strong(scheduleHintOnPresent, + NotifyExpectedPresentHintStatus::Sent)) { + sendHint(); + } +} + +void SurfaceFlinger::sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) { + if (auto itr = mNotifyExpectedPresentMap.find(displayId); + itr == mNotifyExpectedPresentMap.end() || + itr->second.hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnPresent) { + return; + } + scheduleNotifyExpectedPresentHint(displayId); } void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { @@ -4206,10 +4361,6 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { const auto activeMode = display->refreshRateSelector().getActiveMode(); const Fps activeRefreshRate = activeMode.fps; - mRefreshRateStats = - std::make_unique<RefreshRateStats>(*mTimeStats, activeRefreshRate, hal::PowerMode::OFF); - - mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate); FeatureFlags features; @@ -4235,48 +4386,43 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { if (mBackpressureGpuComposition) { features |= Feature::kBackpressureGpuComposition; } - - auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs()); + if (getHwComposer().getComposer()->isSupported( + Hwc2::Composer::OptionalFeature::ExpectedPresentTime)) { + features |= Feature::kExpectedPresentTime; + } mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features, - std::move(modulatorPtr), - static_cast<IVsyncTrackerCallback&>(*this)); + getFactory(), activeRefreshRate, *mTimeStats); + + // 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->startTimers(); - const auto configs = mVsyncConfiguration->getCurrentConfigs(); + const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs(); - mAppConnectionHandle = - mScheduler->createEventThread(Scheduler::Cycle::Render, - mFrameTimeline->getTokenManager(), - /* workDuration */ configs.late.appWorkDuration, - /* readyDuration */ configs.late.sfWorkDuration); - mSfConnectionHandle = - mScheduler->createEventThread(Scheduler::Cycle::LastComposite, - mFrameTimeline->getTokenManager(), - /* workDuration */ activeRefreshRate.getPeriod(), - /* readyDuration */ configs.late.sfWorkDuration); + mScheduler->createEventThread(scheduler::Cycle::Render, mFrameTimeline->getTokenManager(), + /* workDuration */ configs.late.appWorkDuration, + /* readyDuration */ configs.late.sfWorkDuration); + mScheduler->createEventThread(scheduler::Cycle::LastComposite, + mFrameTimeline->getTokenManager(), + /* workDuration */ activeRefreshRate.getPeriod(), + /* readyDuration */ configs.late.sfWorkDuration); - mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), - *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration); + // Dispatch after EventThread creation, since registerDisplay above skipped dispatch. + mScheduler->dispatchHotplug(display->getPhysicalId(), scheduler::Scheduler::Hotplug::Connected); + + mScheduler->initVsync(*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration); mRegionSamplingThread = sp<RegionSamplingThread>::make(*this, RegionSamplingThread::EnvironmentTimingTunables()); - mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this); - - mIsHotplugErrViaNegVsync = - base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false); -} + mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline); -void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) { - mVsyncConfiguration->setRefreshRateFps(refreshRate); - mScheduler->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs(), - refreshRate.getPeriod()); + // Timer callbacks may fire, so do this last. + mScheduler->startTimers(); } void SurfaceFlinger::doCommitTransactions() { @@ -4310,7 +4456,6 @@ void SurfaceFlinger::doCommitTransactions() { } mDrawingState = mCurrentState; - // clear the "changed" flags in current state mCurrentState.colorMatrixChanged = false; if (mVisibleRegionsDirty) { @@ -4458,7 +4603,7 @@ status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinde if (mNumLayers >= MAX_LAYERS) { ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(), MAX_LAYERS); - static_cast<void>(mScheduler->schedule([=] { + static_cast<void>(mScheduler->schedule([=, this] { ALOGE("Dumping layer keeping > 20 children alive:"); bool leakingParentLayerFound = false; mDrawingState.traverse([&](Layer* layer) { @@ -4577,7 +4722,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; @@ -4585,8 +4737,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; @@ -5004,6 +5155,11 @@ status_t SurfaceFlinger::setTransactionState( const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone; mTransactionHandler.queueTransaction(std::move(state)); + for (const auto& [displayId, data] : mNotifyExpectedPresentMap) { + if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) { + scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId}); + } + } setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint); return NO_ERROR; } @@ -5452,6 +5608,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime flags |= eTraversalNeeded; } } + if (what & layer_state_t::eDesiredHdrHeadroomChanged) { + if (layer->setDesiredHdrHeadroom(s.desiredHdrSdrRatio)) { + flags |= eTraversalNeeded; + } + } if (what & layer_state_t::eCachingHintChanged) { if (layer->setCachingHint(s.cachingHint)) { flags |= eTraversalNeeded; @@ -5637,6 +5798,11 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f flags |= eTraversalNeeded; } } + if (what & layer_state_t::eDesiredHdrHeadroomChanged) { + if (layer->setDesiredHdrHeadroom(s.desiredHdrSdrRatio)) { + flags |= eTraversalNeeded; + } + } if (what & layer_state_t::eBufferChanged) { std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt; frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence); @@ -5769,7 +5935,7 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurface case ISurfaceComposerClient::eFXSurfaceContainer: case ISurfaceComposerClient::eFXSurfaceBufferState: args.flags |= ISurfaceComposerClient::eNoColorFill; - FMT_FALLTHROUGH; + [[fallthrough]]; case ISurfaceComposerClient::eFXSurfaceEffect: { result = createBufferStateLayer(args, &outResult.handle, &layer); std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter(); @@ -5847,12 +6013,6 @@ void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32 } void SurfaceFlinger::initializeDisplays() { - const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); - if (!display) return; - - const sp<IBinder> token = display->getDisplayToken().promote(); - LOG_ALWAYS_FATAL_IF(token == nullptr); - TransactionState state; state.inputWindowCommands = mInputWindowCommands; const nsecs_t now = systemTime(); @@ -5863,18 +6023,10 @@ void SurfaceFlinger::initializeDisplays() { const uint64_t transactionId = (static_cast<uint64_t>(mPid) << 32) | mUniqueTransactionId++; state.id = transactionId; - // reset screen orientation and use primary layer stack - DisplayState d; - d.what = DisplayState::eDisplayProjectionChanged | - DisplayState::eLayerStackChanged; - d.token = token; - d.layerStack = ui::DEFAULT_LAYER_STACK; - d.orientation = ui::ROTATION_0; - d.orientedDisplaySpaceRect.makeInvalid(); - d.layerStackSpaceRect.makeInvalid(); - d.width = 0; - d.height = 0; - state.displays.add(d); + auto layerStack = ui::DEFAULT_LAYER_STACK.id; + for (const auto& [id, display] : FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)) { + state.displays.push(DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++))); + } std::vector<TransactionState> transactions; transactions.emplace_back(state); @@ -5888,12 +6040,25 @@ void SurfaceFlinger::initializeDisplays() { { ftl::FakeGuard guard(mStateLock); - setPowerModeInternal(display, hal::PowerMode::ON); + + // In case of a restart, ensure all displays are off. + for (const auto& [id, display] : mPhysicalDisplays) { + setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::OFF); + } + + // Power on all displays. The primary display is first, so becomes the active display. Also, + // the DisplayCapability set of a display is populated on its first powering on. Do this now + // before responding to any Binder query from DisplayManager about display capabilities. + for (const auto& [id, display] : mPhysicalDisplays) { + setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON); + } } } void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) { if (display->isVirtual()) { + // TODO(b/241285876): This code path should not be reachable, so enforce this at compile + // time. ALOGE("%s: Invalid operation on virtual display", __func__); return; } @@ -5901,8 +6066,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: const auto displayId = display->getPhysicalId(); ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str()); - const auto currentModeOpt = display->getPowerMode(); - if (currentModeOpt == mode) { + const auto currentMode = display->getPowerMode(); + if (currentMode == mode) { return; } @@ -5919,7 +6084,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: display->setPowerMode(mode); const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr; - if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) { + if (currentMode == hal::PowerMode::OFF) { // Turn on the display // Activate the display (which involves a modeset to the active mode) when the inner or @@ -5964,7 +6129,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mVisibleRegionsDirty = true; scheduleComposite(FrameHint::kActive); } else if (mode == hal::PowerMode::OFF) { - const bool currentModeNotDozeSuspend = (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND); + const bool currentModeNotDozeSuspend = (currentMode != hal::PowerMode::DOZE_SUSPEND); // Turn off the display if (displayId == mActiveDisplayId) { if (const auto display = getActivatableDisplay()) { @@ -6005,7 +6170,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) { // Update display while dozing getHwComposer().setPowerMode(displayId, mode); - if (*currentModeOpt == hal::PowerMode::DOZE_SUSPEND && + if (currentMode == hal::PowerMode::DOZE_SUSPEND && (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) { if (displayId == mActiveDisplayId) { ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON."); @@ -6033,7 +6198,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (displayId == mActiveDisplayId) { mTimeStats->setPowerMode(mode); - mRefreshRateStats->setPowerMode(mode); + mScheduler->setActiveDisplayPowerModeForRefreshRateStats(mode); } mScheduler->setDisplayPowerMode(displayId, mode); @@ -6042,7 +6207,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( kMainThreadContext) { const auto display = getDisplayDeviceLocked(displayToken); if (!display) { @@ -6198,10 +6363,6 @@ void SurfaceFlinger::dumpScheduler(std::string& result) const { dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor); dumper.eol(); - mRefreshRateStats->dump(result); - dumper.eol(); - - mVsyncConfiguration->dump(result); StringAppendF(&result, " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", @@ -6209,7 +6370,7 @@ void SurfaceFlinger::dumpScheduler(std::string& result) const { } void SurfaceFlinger::dumpEvents(std::string& result) const { - mScheduler->dump(mAppConnectionHandle, result); + mScheduler->dump(scheduler::Cycle::Render, result); } void SurfaceFlinger::dumpVsync(std::string& result) const { @@ -6496,7 +6657,7 @@ void SurfaceFlinger::dumpOffscreenLayersProto(perfetto::protos::LayersProto& lay } perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) { - return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get(); + return mScheduler->schedule([=, this] { return dumpDrawingStateProto(traceFlags); }).get(); } void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { @@ -6986,14 +7147,15 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r mForceFullDamage = n != 0; return NO_ERROR; } - case 1018: { // Modify Choreographer's duration + case 1018: { // Set the render deadline as a duration until VSYNC. n = data.readInt32(); - mScheduler->setDuration(mAppConnectionHandle, std::chrono::nanoseconds(n), 0ns); + mScheduler->setDuration(scheduler::Cycle::Render, std::chrono::nanoseconds(n), 0ns); return NO_ERROR; } - case 1019: { // Modify SurfaceFlinger's duration + case 1019: { // Set the deadline of the last composite as a duration until VSYNC. n = data.readInt32(); - mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns); + mScheduler->setDuration(scheduler::Cycle::LastComposite, + std::chrono::nanoseconds(n), 0ns); return NO_ERROR; } case 1020: { // Unused @@ -7225,7 +7387,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r auto inUid = static_cast<uid_t>(data.readInt32()); const auto refreshRate = data.readFloat(); mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate}); - mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); + mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); return NO_ERROR; } // Toggle caching feature @@ -7417,7 +7579,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { // Update the overlay on the main thread to avoid race conditions with // RefreshRateSelector::getActiveMode - static_cast<void>(mScheduler->schedule([=] { + static_cast<void>(mScheduler->schedule([=, this] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); if (!display) { ALOGW("%s: default display is null", __func__); @@ -7583,7 +7745,7 @@ status_t SurfaceFlinger::setSchedFifo(bool enabled) { status_t SurfaceFlinger::setSchedAttr(bool enabled) { static const unsigned int kUclampMin = - base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min", 0U); + base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min"s, 0U); if (!kUclampMin) { // uclamp.min set to 0 (default), skip setting @@ -7773,6 +7935,12 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args kAllowProtected, kGrayscale, captureListener); } +ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) { + sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make(); + captureLayers(args, captureListener); + return captureListener->waitForResults(); +} + void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { ATRACE_CALL(); @@ -7844,7 +8012,7 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } bool childrenOnly = args.childrenOnly; - RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> { + RenderAreaFuture renderAreaFuture = ftl::defer([=, this]() -> std::unique_ptr<RenderArea> { ui::Transform layerTransform; Rect layerBufferSize; if (mLayerLifecycleManagerEnabled) { @@ -7983,7 +8151,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( ATRACE_CALL(); auto future = mScheduler->schedule( - [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD( + [=, this, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD( kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { ScreenCaptureResults captureResults; std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get(); @@ -8182,23 +8350,27 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( // // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call // to CompositionEngine::present. - const bool renderEngineIsThreaded = [&]() { - using Type = renderengine::RenderEngine::RenderEngineType; - const auto type = mRenderEngine->getRenderEngineType(); - return type == Type::SKIA_GL_THREADED; - }(); - auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share() - : ftl::yield(present()).share(); + auto presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share() + : ftl::yield(present()).share(); for (auto& [layer, layerFE] : layers) { - layer->onLayerDisplayed(ftl::Future(presentFuture) - .then([layerFE = std::move(layerFE)](FenceResult) { - return layerFE->stealCompositionResult() - .releaseFences.back() - .first.get(); - }) - .share(), - ui::INVALID_LAYER_STACK); + layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK, + [layerFE = std::move(layerFE)](FenceResult) { + if (FlagManager::getInstance() + .screenshot_fence_preservation()) { + const auto compositionResult = + layerFE->stealCompositionResult(); + const auto& fences = compositionResult.releaseFences; + // CompositionEngine may choose to cull layers that + // aren't visible, so pass a non-fence. + return fences.empty() ? Fence::NO_FENCE + : fences.back().first.get(); + } else { + return layerFE->stealCompositionResult() + .releaseFences.back() + .first.get(); + } + }); } return presentFuture; @@ -8341,10 +8513,10 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might // be depending in this callback. if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) { - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); + mScheduler->onPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode); toggleKernelIdleTimer(); } else { - mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); + mScheduler->onNonPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode); } auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode); @@ -8357,15 +8529,15 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( const auto preferredModeId = preferredMode.modePtr->getId(); const Fps preferredFps = preferredMode.fps; - ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(), + ALOGV("Switching to Scheduler preferred mode %d (%s)", ftl::to_underlying(preferredModeId), to_string(preferredFps).c_str()); if (!selector.isModeAllowed(preferredMode)) { - ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value()); + ALOGE("%s: Preferred mode %d is disallowed", __func__, ftl::to_underlying(preferredModeId)); return INVALID_OPERATION; } - setDesiredMode({std::move(preferredMode), .emitEvent = true}, force); + setDesiredMode({std::move(preferredMode), .emitEvent = true, .force = force}); // Update the frameRateOverride list as the display render rate might have changed if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) { @@ -8408,7 +8580,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return BAD_VALUE; } - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken)); if (!display) { ALOGE("Attempt to set desired display modes for invalid display token %p", @@ -8449,7 +8621,7 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo scheduler::RefreshRateSelector::Policy policy = display->refreshRateSelector().getDisplayManagerPolicy(); - outSpecs->defaultMode = policy.defaultMode.value(); + outSpecs->defaultMode = ftl::to_underlying(policy.defaultMode); outSpecs->allowGroupSwitching = policy.allowGroupSwitching; outSpecs->primaryRanges = translate(policy.primaryRanges); outSpecs->appRequestRanges = translate(policy.appRequestRanges); @@ -8532,7 +8704,7 @@ status_t SurfaceFlinger::setGameModeFrameRateOverride(uid_t uid, float frameRate }(); mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate}); - mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); + mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); return NO_ERROR; } @@ -8632,7 +8804,8 @@ uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t ui } int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const { - const auto vsyncConfig = mVsyncConfiguration->getConfigsForRefreshRate(refreshRate).late; + const auto vsyncConfig = + mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(refreshRate).late; const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration; return calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } @@ -8687,7 +8860,11 @@ void SurfaceFlinger::sample() { return; } - mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime()); + const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult(); + const auto scheduleFrameTimeOpt = scheduledFrameResultOpt + ? std::optional{scheduledFrameResultOpt->callbackTime} + : std::nullopt; + mRegionSamplingThread->onCompositionComplete(scheduleFrameTimeOpt); } void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) { @@ -8725,7 +8902,7 @@ void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveD mActiveDisplayId = activeDisplay.getPhysicalId(); activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true); - resetPhaseConfiguration(activeDisplay.getActiveMode().fps); + mScheduler->resetPhaseConfiguration(activeDisplay.getActiveMode().fps); // TODO(b/255635711): Check for pending mode changes on other displays. mScheduler->setModeChangePending(false); @@ -8763,6 +8940,41 @@ status_t SurfaceFlinger::getStalledTransactionInfo( return NO_ERROR; } +void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, + int32_t maxLevel) { + if (!FlagManager::getInstance().connected_display()) { + return; + } + + Mutex::Autolock lock(mStateLock); + + const auto idOpt = getHwComposer().toPhysicalDisplayId(hwcDisplayId); + if (!idOpt) { + ALOGE("No display found for HDCP level changed event: connected=%d, max=%d for " + "display=%" PRIu64, + connectedLevel, maxLevel, hwcDisplayId); + return; + } + + const bool isInternalDisplay = + mPhysicalDisplays.get(*idOpt).transform(&PhysicalDisplay::isInternal).value_or(false); + if (isInternalDisplay) { + ALOGW("Unexpected HDCP level changed for internal display: connected=%d, max=%d for " + "display=%" PRIu64, + connectedLevel, maxLevel, hwcDisplayId); + return; + } + + static_cast<void>(mScheduler->schedule([this, displayId = *idOpt, connectedLevel, maxLevel]() { + if (const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayId))) { + Mutex::Autolock lock(mStateLock); + display->setSecure(connectedLevel >= 2 /* HDCP_V1 */); + } + mScheduler->onHdcpLevelsChanged(scheduler::Cycle::Render, displayId, connectedLevel, + maxLevel); + })); +} + std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData( BufferData& bufferData, const char* layerName, uint64_t transactionId) { if (bufferData.buffer && @@ -9582,6 +9794,12 @@ binder::Status SurfaceComposerAIDL::captureDisplayById( return binderStatusFromStatusT(NO_ERROR); } +binder::Status SurfaceComposerAIDL::captureLayersSync(const LayerCaptureArgs& args, + ScreenCaptureResults* outResults) { + *outResults = mFlinger->captureLayersSync(args); + return binderStatusFromStatusT(NO_ERROR); +} + binder::Status SurfaceComposerAIDL::captureLayers( const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { mFlinger->captureLayers(args, captureListener); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 61360a70df..afc76471f4 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -82,7 +82,6 @@ #include "MutexUtils.h" #include "Scheduler/ISchedulerCallback.h" #include "Scheduler/RefreshRateSelector.h" -#include "Scheduler/RefreshRateStats.h" #include "Scheduler/Scheduler.h" #include "SurfaceFlingerFactory.h" #include "ThreadContext.h" @@ -200,8 +199,7 @@ class SurfaceFlinger : public BnSurfaceComposer, private HWC2::ComposerCallback, private ICompositor, private scheduler::ISchedulerCallback, - private compositionengine::ICEPowerCallback, - private scheduler::IVsyncTrackerCallback { + private compositionengine::ICEPowerCallback { public: struct SkipInitializationTag {}; @@ -561,6 +559,7 @@ private: void captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); void captureDisplay(DisplayId, const CaptureArgs&, const sp<IScreenCaptureListener>&); + ScreenCaptureResults captureLayersSync(const LayerCaptureArgs&); void captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats); @@ -654,6 +653,8 @@ private: status_t getStalledTransactionInfo( int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result); + void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel); + // Implements IBinder::DeathRecipient. void binderDied(const wp<IBinder>& who) override; @@ -684,14 +685,12 @@ private: void kernelTimerChanged(bool expired) override; void triggerOnFrameRateOverridesChanged() override; void onChoreographerAttached() override; + void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, + Fps renderRate) override; // ICEPowerCallback overrides: void notifyCpuLoadUp() override; - // IVsyncTrackerCallback overrides - void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, - Fps renderRate) override; - // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); @@ -717,7 +716,7 @@ private: // Show hdr sdr ratio overlay bool mHdrSdrRatioOverlay = false; - void setDesiredMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock); + void setDesiredMode(display::DisplayModeRequest&&) REQUIRES(mStateLock); status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps, Fps maxFps); @@ -784,9 +783,6 @@ private: void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock); - void resetPhaseConfiguration(Fps) REQUIRES(mStateLock, kMainThreadContext); - void updatePhaseConfiguration(Fps) REQUIRES(mStateLock); - /* * Transactions */ @@ -906,7 +902,8 @@ private: * Display and layer stack management */ - // Called during boot, and restart after system_server death. + // Called during boot and restart after system_server death, setting the stage for bootanimation + // before DisplayManager takes over. void initializeDisplays() REQUIRES(kMainThreadContext); sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const @@ -1054,7 +1051,6 @@ private: const DisplayDeviceState& drawingState) REQUIRES(mStateLock, kMainThreadContext); - void dispatchDisplayHotplugEvent(PhysicalDisplayId, bool connected); void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&) REQUIRES(mStateLock); @@ -1215,12 +1211,6 @@ private: float mGlobalSaturationFactor = 1.0f; mat4 mClientColorMatrix; - size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS; - // If there are more GraphicBufferProducers tracked by SurfaceFlinger than - // this threshold, then begin logging. - size_t mGraphicBufferProducerListSizeLogThreshold = - static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS)); - // protected by mStateLock (but we could use another lock) bool mLayersRemoved = false; bool mLayersAdded = false; @@ -1277,6 +1267,7 @@ private: hal::Connection connection = hal::Connection::INVALID; }; + bool mIsHdcpViaNegVsync = false; bool mIsHotplugErrViaNegVsync = false; std::mutex mHotplugMutex; @@ -1365,17 +1356,8 @@ private: const std::string mHwcServiceName; - /* - * Scheduler - */ std::unique_ptr<scheduler::Scheduler> mScheduler; - scheduler::ConnectionHandle mAppConnectionHandle; - scheduler::ConnectionHandle mSfConnectionHandle; - - // Stores phase offsets configured per refresh rate. - std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration; - std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext); bool mLumaSampling = true; @@ -1470,7 +1452,7 @@ private: bool mLegacyFrontEndEnabled = true; frontend::LayerLifecycleManager mLayerLifecycleManager; - frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}}; + frontend::LayerHierarchyBuilder mLayerHierarchyBuilder; frontend::LayerSnapshotBuilder mLayerSnapshotBuilder; std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles; @@ -1492,14 +1474,30 @@ private: ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug; // NotifyExpectedPresentHint + enum class NotifyExpectedPresentHintStatus { + // Represents that framework can start sending hint if required. + Start, + // Represents that the hint is already sent. + Sent, + // Represents that the hint will be scheduled with a new frame. + ScheduleOnPresent, + // Represents that a hint will be sent instantly by scheduling on the main thread. + ScheduleOnTx + }; struct NotifyExpectedPresentData { - // lastExpectedPresentTimestamp is read and write from multiple threads such as - // main thread, EventThread, MessageQueue. And is atomic for that reason. - std::atomic<TimePoint> lastExpectedPresentTimestamp{}; + TimePoint lastExpectedPresentTimestamp{}; Fps lastFrameInterval{}; + // hintStatus is read and write from multiple threads such as + // main thread, EventThread. And is atomic for that reason. + std::atomic<NotifyExpectedPresentHintStatus> hintStatus = + NotifyExpectedPresentHintStatus::Start; }; std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap; - + void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) override + REQUIRES(kMainThreadContext); + void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId, + VsyncId vsyncId = VsyncId{ + FrameTimelineInfo::INVALID_VSYNC_ID}); void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, TimePoint expectedPresentTime, Fps frameInterval, std::optional<Period> timeoutOpt); @@ -1558,6 +1556,7 @@ public: const sp<IScreenCaptureListener>&) override; binder::Status captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override; + binder::Status captureLayersSync(const LayerCaptureArgs&, ScreenCaptureResults* results); // TODO(b/239076119): Remove deprecated AIDL. [[deprecated]] binder::Status clearAnimationFrameStats() override { diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp index c3141be9db..a631074f4d 100644 --- a/services/surfaceflinger/TimeStats/Android.bp +++ b/services/surfaceflinger/TimeStats/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp index 0cf086fe01..be65510279 100644 --- a/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp +++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library { @@ -33,4 +34,4 @@ cc_library { "-Wno-undef", "-Wno-unused-parameter", ], -}
\ No newline at end of file +} diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp index 972edaa30b..e097da399d 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp +++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library { diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h index cb96e05680..7a0fb5ef6e 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.h +++ b/services/surfaceflinger/Tracing/TransactionTracing.h @@ -219,6 +219,13 @@ class TransactionTraceWriter : public Singleton<TransactionTraceWriter> { friend class Singleton<TransactionTracing>; std::function<void(const std::string& prefix, bool overwrite)> mWriterFunction = [](const std::string&, bool) {}; + std::atomic<bool> mEnabled{true}; + + void doInvoke(const std::string& filename, bool overwrite) { + if (mEnabled) { + mWriterFunction(filename, overwrite); + } + }; public: void setWriterFunction( @@ -226,12 +233,15 @@ public: mWriterFunction = std::move(function); } void invoke(const std::string& prefix, bool overwrite) { - mWriterFunction(TransactionTracing::getFilePath(prefix), overwrite); + doInvoke(TransactionTracing::getFilePath(prefix), overwrite); } /* pass in a complete file path for testing */ void invokeForTest(const std::string& filename, bool overwrite) { - mWriterFunction(filename, overwrite); + doInvoke(filename, overwrite); } + /* hacky way to avoid generating traces when converting transaction trace to layers trace. */ + void disable() { mEnabled.store(false); } + void enable() { mEnabled.store(true); } }; } // namespace android diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp index 2ff09c3f4c..8afca4139c 100644 --- a/services/surfaceflinger/Tracing/tools/Android.bp +++ b/services/surfaceflinger/Tracing/tools/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_binary { diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp index c2d1954ee5..617ea2c566 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -40,9 +40,24 @@ namespace android { using namespace ftl::flag_operators; +namespace { +class ScopedTraceDisabler { +public: + ScopedTraceDisabler() { TransactionTraceWriter::getInstance().disable(); } + ~ScopedTraceDisabler() { TransactionTraceWriter::getInstance().enable(); } +}; +} // namespace + bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& traceFile, std::uint32_t traceFlags, LayerTracing& layerTracing, bool onlyLastEntry) { + // We are generating the layers trace by replaying back a set of transactions. If the + // transactions have unexpected states, we may generate a transaction trace to debug + // the unexpected state. This is silly. So we disable it by poking the + // TransactionTraceWriter. This is really a hack since we should manage our depenecies a + // little better. + ScopedTraceDisabler fatalErrorTraceDisabler; + if (traceFile.entry_size() == 0) { ALOGD("Trace file is empty"); return false; @@ -52,7 +67,7 @@ bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& // frontend frontend::LayerLifecycleManager lifecycleManager; - frontend::LayerHierarchyBuilder hierarchyBuilder{{}}; + frontend::LayerHierarchyBuilder hierarchyBuilder; frontend::LayerSnapshotBuilder snapshotBuilder; ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos; @@ -119,12 +134,10 @@ bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& lifecycleManager.applyTransactions(transactions, /*ignoreUnknownHandles=*/true); lifecycleManager.onHandlesDestroyed(destroyedHandles, /*ignoreUnknownHandles=*/true); - if (lifecycleManager.getGlobalChanges().test( - frontend::RequestedLayerState::Changes::Hierarchy)) { - hierarchyBuilder.update(lifecycleManager.getLayers(), - lifecycleManager.getDestroyedLayers()); - } + // update hierarchy + hierarchyBuilder.update(lifecycleManager); + // update snapshots frontend::LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(), .layerLifecycleManager = lifecycleManager, .displays = displayInfos, diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index 6a155c17df..7b5298c82e 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -25,6 +25,7 @@ #include "TransactionCallbackInvoker.h" #include "BackgroundExecutor.h" +#include "Utils/FenceUtils.h" #include <cinttypes> @@ -127,33 +128,8 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& sp<IBinder> surfaceControl = handle->surfaceControl.promote(); if (surfaceControl) { sp<Fence> prevFence = nullptr; - for (const auto& future : handle->previousReleaseFences) { - sp<Fence> currentFence = future.get().value_or(Fence::NO_FENCE); - if (prevFence == nullptr && currentFence->getStatus() != Fence::Status::Invalid) { - prevFence = std::move(currentFence); - } else if (prevFence != nullptr) { - // If both fences are signaled or both are unsignaled, we need to merge - // them to get an accurate timestamp. - if (prevFence->getStatus() != Fence::Status::Invalid && - prevFence->getStatus() == currentFence->getStatus()) { - char fenceName[32] = {}; - snprintf(fenceName, 32, "%.28s", handle->name.c_str()); - sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, currentFence); - if (mergedFence->isValid()) { - prevFence = std::move(mergedFence); - } - } else if (currentFence->getStatus() == Fence::Status::Unsignaled) { - // If one fence has signaled and the other hasn't, the unsignaled - // fence will approximately correspond with the correct timestamp. - // There's a small race if both fences signal at about the same time - // and their statuses are retrieved with unfortunate timing. However, - // by this point, they will have both signaled and only the timestamp - // will be slightly off; any dependencies after this point will - // already have been met. - prevFence = std::move(currentFence); - } - } + mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence); } handle->previousReleaseFence = prevFence; handle->previousReleaseFences.clear(); diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h index ee942177e5..62d2ebb76d 100644 --- a/services/surfaceflinger/Utils/Dumper.h +++ b/services/surfaceflinger/Utils/Dumper.h @@ -35,6 +35,8 @@ public: void eol() { mOut += '\n'; } + std::string& out() { return mOut; } + void dump(std::string_view name, std::string_view value = {}) { using namespace std::string_view_literals; diff --git a/services/surfaceflinger/Utils/FenceUtils.h b/services/surfaceflinger/Utils/FenceUtils.h new file mode 100644 index 0000000000..f6f70062df --- /dev/null +++ b/services/surfaceflinger/Utils/FenceUtils.h @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <ui/Fence.h> + +namespace android { + +// TODO: measure if Fence::merge is cheaper +inline void mergeFence(const char* debugName, sp<Fence>&& incomingFence, sp<Fence>& prevFence) { + if (prevFence == nullptr && incomingFence->getStatus() != Fence::Status::Invalid) { + prevFence = std::move(incomingFence); + } else if (prevFence != nullptr) { + // If both fences are signaled or both are unsignaled, we need to merge + // them to get an accurate timestamp. + if (prevFence->getStatus() != Fence::Status::Invalid && + prevFence->getStatus() == incomingFence->getStatus()) { + char fenceName[32] = {}; + snprintf(fenceName, 32, "%.28s", debugName); + sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, incomingFence); + if (mergedFence->isValid()) { + prevFence = std::move(mergedFence); + } + } else if (incomingFence->getStatus() == Fence::Status::Unsignaled) { + // If one fence has signaled and the other hasn't, the unsignaled + // fence will approximately correspond with the correct timestamp. + // There's a small race if both fences signal at about the same time + // and their statuses are retrieved with unfortunate timing. However, + // by this point, they will have both signaled and only the timestamp + // will be slightly off; any dependencies after this point will + // already have been met. + prevFence = std::move(incomingFence); + } + } +} + +} // namespace android diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp index 5ef22b590b..f2ff00b842 100644 --- a/services/surfaceflinger/common/Android.bp +++ b/services/surfaceflinger/common/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { @@ -18,7 +19,7 @@ cc_defaults { "server_configurable_flags", ], static_libs: [ - "librenderengine", + "librenderengine_includes", ], srcs: [ "FlagManager.cpp", diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index cb1faee6b9..b7f06a992e 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -108,8 +108,6 @@ void FlagManager::dump(std::string& result) const { DUMP_SERVER_FLAG(use_skia_tracing); /// Trunk stable server flags /// - DUMP_SERVER_FLAG(late_boot_misc2); - DUMP_SERVER_FLAG(dont_skip_on_early); DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display); /// Trunk stable readonly flags /// @@ -122,12 +120,18 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(multithreaded_present); DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace); DUMP_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency); - DUMP_READ_ONLY_FLAG(cache_if_source_crop_layer_only_moved); + DUMP_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved); DUMP_READ_ONLY_FLAG(enable_fro_dependent_features); DUMP_READ_ONLY_FLAG(display_protected); DUMP_READ_ONLY_FLAG(fp16_client_target); DUMP_READ_ONLY_FLAG(game_default_frame_rate); DUMP_READ_ONLY_FLAG(enable_layer_command_batching); + DUMP_READ_ONLY_FLAG(screenshot_fence_preservation); + DUMP_READ_ONLY_FLAG(vulkan_renderengine); + DUMP_READ_ONLY_FLAG(renderable_buffer_usage); + DUMP_READ_ONLY_FLAG(restore_blur_step); + DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro); + DUMP_READ_ONLY_FLAG(protected_if_client); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG #undef DUMP_FLAG_INTERVAL @@ -195,23 +199,21 @@ FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "") FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present") FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "") FLAG_MANAGER_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency, "") -FLAG_MANAGER_READ_ONLY_FLAG(cache_if_source_crop_layer_only_moved, +FLAG_MANAGER_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved, "debug.sf.cache_source_crop_only_moved") FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "") FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "") FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target") FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "") +FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation") +FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan") +FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "") +FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step") +FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "") +FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "") /// Trunk stable server flags /// -FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "") FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") -/// Exceptions /// -bool FlagManager::dont_skip_on_early() const { - // Even though this is a server writable flag, we do call it before boot completed, but that's - // fine since the decision is done per frame. We can't do caching though. - return flags::dont_skip_on_early(); -} - } // namespace android diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 8b5983d2c4..241c814780 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -48,8 +48,6 @@ public: bool use_skia_tracing() const; /// Trunk stable server flags /// - bool late_boot_misc2() const; - bool dont_skip_on_early() const; bool refresh_rate_overlay_on_external_display() const; /// Trunk stable readonly flags /// @@ -62,12 +60,18 @@ public: bool multithreaded_present() const; bool add_sf_skipped_frames_to_trace() const; bool use_known_refresh_rate_for_fps_consistency() const; - bool cache_if_source_crop_layer_only_moved() const; + bool cache_when_source_crop_layer_only_moved() const; bool enable_fro_dependent_features() const; bool display_protected() const; bool fp16_client_target() const; bool game_default_frame_rate() const; bool enable_layer_command_batching() const; + bool screenshot_fence_preservation() const; + bool vulkan_renderengine() const; + bool renderable_buffer_usage() const; + bool restore_blur_step() const; + bool dont_skip_on_early_ro() const; + bool protected_if_client() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp index ab3b3528dd..ae502cf4b4 100644 --- a/services/surfaceflinger/fuzzer/Android.bp +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -22,50 +22,23 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { name: "surfaceflinger_fuzz_defaults", - include_dirs: [ - "frameworks/native/services/surfaceflinger/tests/unittests", - ], static_libs: [ - "android.hardware.graphics.composer@2.1-resources", "libc++fs", - "libgmock", - "libgui_mocks", - "libgmock_ndk", - "libgmock_main", - "libgtest_ndk_c++", - "libgmock_main_ndk", - "librenderengine_mocks", "libsurfaceflinger_common", - "perfetto_trace_protos", - "libcompositionengine_mocks", - "perfetto_trace_protos", - ], - shared_libs: [ - "libprotoutil", - "libstatssocket", - "libstatspull", - "libtimestats", - "libtimestats_proto", - "libprotobuf-cpp-full", - "android.hardware.graphics.mapper@2.0", - "android.hardware.graphics.mapper@3.0", - "android.hardware.graphics.mapper@4.0", ], srcs: [ ":libsurfaceflinger_sources", - ":libsurfaceflinger_mock_sources", ], defaults: [ "libsurfaceflinger_defaults", ], header_libs: [ - "libui_fuzzableDataspaces_headers", "libsurfaceflinger_headers", - "libui_headers", ], cflags: [ "-Wno-unused-result", @@ -90,66 +63,6 @@ cc_defaults { } cc_fuzz { - name: "surfaceflinger_fuzzer", - defaults: [ - "surfaceflinger_fuzz_defaults", - ], - srcs: [ - "surfaceflinger_fuzzer.cpp", - ], -} - -cc_fuzz { - name: "surfaceflinger_displayhardware_fuzzer", - defaults: [ - "surfaceflinger_fuzz_defaults", - ], - srcs: [ - "surfaceflinger_displayhardware_fuzzer.cpp", - ], - header_libs: [ - "android.hardware.graphics.composer@2.4-command-buffer", - "android.hardware.graphics.composer@2.4-hal", - ], -} - -cc_fuzz { - name: "surfaceflinger_scheduler_fuzzer", - defaults: [ - "surfaceflinger_fuzz_defaults", - ], - srcs: [ - "surfaceflinger_scheduler_fuzzer.cpp", - ], -} - -cc_fuzz { - name: "surfaceflinger_layer_fuzzer", - defaults: [ - "surfaceflinger_fuzz_defaults", - ], - header_libs: [ - "libgui_headers", - ], - static_libs: [ - "librenderengine", - ], - srcs: [ - "surfaceflinger_layer_fuzzer.cpp", - ], -} - -cc_fuzz { - name: "surfaceflinger_frametracer_fuzzer", - defaults: [ - "surfaceflinger_fuzz_defaults", - ], - srcs: [ - "surfaceflinger_frametracer_fuzzer.cpp", - ], -} - -cc_fuzz { name: "surfaceflinger_service_fuzzer", defaults: [ "surfaceflinger_fuzz_defaults", diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md deleted file mode 100644 index a06c41b139..0000000000 --- a/services/surfaceflinger/fuzzer/README.md +++ /dev/null @@ -1,108 +0,0 @@ -# Fuzzers for SurfaceFlinger -## Table of contents -+ [SurfaceFlinger](#SurfaceFlinger) -+ [DisplayHardware](#DisplayHardware) -+ [Scheduler](#Scheduler) -+ [Layer](#Layer) -+ [FrameTracer](#FrameTracer) - -# <a name="SurfaceFlinger"></a> Fuzzer for SurfaceFlinger - -SurfaceFlinger supports the following data sources: -1. Pixel Formats (parameter name: `defaultCompositionPixelFormat`) -2. Data Spaces (parameter name: `defaultCompositionDataspace`) -3. Rotations (parameter name: `internalDisplayOrientation`) -3. Surface composer tags (parameter name: `onTransact`) - -You can find the possible values in the fuzzer's source code. - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) surfaceflinger_fuzzer -``` -2. To run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/surfaceflinger_fuzzer/surfaceflinger_fuzzer -``` - -# <a name="DisplayHardware"></a> Fuzzer for DisplayHardware - -DisplayHardware supports the following parameters: -1. Hal Capability (parameter name: `hasCapability`) -2. Hal BlendMode (parameter name: `setBlendMode`) -3. Hal Composition (parameter name: `setCompositionType`) -4. Hal Display Capability (parameter name: `hasDisplayCapability`) -5. Composition Types (parameter name: `prepareFrame`) -6. Color Modes (parameter name: `setActiveColorMode`) -7. Render Intents (parameter name: `setActiveColorMode`) -8. Power Modes (parameter name: `setPowerMode`) -9. Content Types (parameter name: `setContentType`) -10. Data Space (parameter name: `setDataspace`) -11. Transforms (parameter name: `setLayerTransform`) - -You can find the possible values in the fuzzer's source code. - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) surfaceflinger_displayhardware_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/surfaceflinger_displayhardware_fuzzer/surfaceflinger_displayhardware_fuzzer -``` - -# <a name="Scheduler"></a> Fuzzer for Scheduler - -Scheduler supports the following parameters: -1. VSync Periods (parameter name: `lowFpsPeriod`) - -You can find the possible values in the fuzzer's source code. - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) surfaceflinger_scheduler_fuzzer -``` -2. To run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/surfaceflinger_scheduler_fuzzer/surfaceflinger_scheduler_fuzzer -``` - -# <a name="Layer"></a> Fuzzer for Layer - -Layer supports the following parameters: -1. Display Connection Types (parameter name: `fakeDisplay`) -2. State Sets (parameter name: `traverseInZOrder`) -3. Disconnect modes (parameter name: `disconnect`) -4. Data Spaces (parameter name: `setDataspace`) - -You can find the possible values in the fuzzer's source code. - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) surfaceflinger_layer_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/surfaceflinger_layer_fuzzer/surfaceflinger_layer_fuzzer -``` - -# <a name="FrameTracer"></a> Fuzzer for FrameTracer - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) surfaceflinger_frametracer_fuzzer -``` -2. To run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/surfaceflinger_frametracer_fuzzer/surfaceflinger_frametracer_fuzzer -``` diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp deleted file mode 100644 index 68237c8dd6..0000000000 --- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/* - * Copyright 2021 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 <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> -#include <binder/ProcessState.h> -#include <compositionengine/impl/OutputCompositionState.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <gui/BLASTBufferQueue.h> -#include <gui/IGraphicBufferProducer.h> -#include <gui/IProducerListener.h> -#include <gui/LayerDebugInfo.h> -#include <gui/SurfaceComposerClient.h> -#include <hidl/ServiceManagement.h> -#include <hwbinder/ProcessState.h> -#include <ui/DisplayIdentification.h> - -#include "DisplayHardware/AidlComposerHal.h" -#include "DisplayHardware/DisplayMode.h" -#include "DisplayHardware/FramebufferSurface.h" -#include "DisplayHardware/HWComposer.h" -#include "DisplayHardware/PowerAdvisor.h" -#include "DisplayHardware/VirtualDisplaySurface.h" -#include "SurfaceFlinger.h" -#include "surfaceflinger_displayhardware_fuzzer_utils.h" - -#include <FuzzableDataspaces.h> - -namespace android::fuzz { - -using namespace android::hardware::graphics::common; -using namespace android::hardware::graphics::composer; -namespace aidl = aidl::android::hardware::graphics::composer3; -namespace hal = android::hardware::graphics::composer::hal; -using Config = hal::V2_1::Config; -using Display = hal::V2_1::Display; -using RenderIntent = V1_1::RenderIntent; -using IComposerClient = hal::V2_4::IComposerClient; -using VsyncPeriodChangeTimeline = hal::V2_4::VsyncPeriodChangeTimeline; -using PerFrameMetadata = IComposerClient::PerFrameMetadata; -using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob; -using Vsync = IComposerClient::Vsync; - -static constexpr hal::Transform kTransforms[] = {hal::Transform::FLIP_H, hal::Transform::FLIP_V, - hal::Transform::ROT_90, hal::Transform::ROT_180, - hal::Transform::ROT_270}; - -static constexpr aidl::Capability kCapability[] = {aidl::Capability::INVALID, - aidl::Capability::SIDEBAND_STREAM, - aidl::Capability::SKIP_CLIENT_COLOR_TRANSFORM, - aidl::Capability::PRESENT_FENCE_IS_NOT_RELIABLE, - aidl::Capability::SKIP_VALIDATE}; - -static constexpr hal::BlendMode kBlendModes[] = {hal::BlendMode::INVALID, hal::BlendMode::NONE, - hal::BlendMode::PREMULTIPLIED, - hal::BlendMode::COVERAGE}; - -static constexpr Composition kCompositions[] = {Composition::INVALID, Composition::CLIENT, - Composition::DEVICE, Composition::SOLID_COLOR, - Composition::CURSOR, Composition::SIDEBAND}; - -static constexpr DisplayCapability kDisplayCapability[] = - {DisplayCapability::INVALID, - DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM, - DisplayCapability::DOZE, - DisplayCapability::BRIGHTNESS, - DisplayCapability::PROTECTED_CONTENTS, - DisplayCapability::AUTO_LOW_LATENCY_MODE}; - -static constexpr VirtualDisplaySurface::CompositionType kCompositionTypes[] = - {VirtualDisplaySurface::CompositionType::Unknown, - VirtualDisplaySurface::CompositionType::Gpu, VirtualDisplaySurface::CompositionType::Hwc, - VirtualDisplaySurface::CompositionType::Mixed}; - -static constexpr ui::RenderIntent kRenderIntents[] = {ui::RenderIntent::COLORIMETRIC, - ui::RenderIntent::ENHANCE, - ui::RenderIntent::TONE_MAP_COLORIMETRIC, - ui::RenderIntent::TONE_MAP_ENHANCE}; - -static constexpr hal::PowerMode kPowerModes[] = {hal::PowerMode::OFF, hal::PowerMode::DOZE, - hal::PowerMode::DOZE_SUSPEND, hal::PowerMode::ON, - hal::PowerMode::ON_SUSPEND}; - -static constexpr hal::ContentType kContentTypes[] = {hal::ContentType::NONE, - hal::ContentType::GRAPHICS, - hal::ContentType::PHOTO, - hal::ContentType::CINEMA, - hal::ContentType::GAME}; - -const unsigned char kInternalEdid[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00" - "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27" - "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" - "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30" - "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53" - "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe" - "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45"; - -static constexpr hal::HWConfigId kActiveConfig = 0; - -class DisplayHardwareFuzzer { -public: - DisplayHardwareFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) { - mPhysicalDisplayId = TestableSurfaceFlinger::getFirstDisplayId().value_or( - PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint8_t>())); - }; - void process(); - -private: - void invokeComposer(); - void invokeDisplayIdentification(); - void invokeLayer(HWC2::Layer* layer); - void setSidebandStream(HWC2::Layer* layer); - void setCursorPosition(HWC2::Layer* layer); - void setBuffer(HWC2::Layer* layer); - void setSurfaceDamage(HWC2::Layer* layer); - void setDisplayFrame(HWC2::Layer* layer); - void setVisibleRegion(HWC2::Layer* layer); - void setLayerGenericMetadata(HWC2::Layer* layer); - void invokeFrameBufferSurface(); - void invokeVirtualDisplaySurface(); - void invokeAidlComposer(); - Display createVirtualDisplay(Hwc2::AidlComposer*); - void validateDisplay(Hwc2::AidlComposer*, Display); - void presentOrValidateDisplay(Hwc2::AidlComposer*, Display); - void setOutputBuffer(Hwc2::AidlComposer*, Display); - void setLayerSidebandStream(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); - void invokeComposerHal2_2(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); - void invokeComposerHal2_3(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); - void invokeComposerHal2_4(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); - void getDisplayVsyncPeriod(); - void setActiveModeWithConstraints(); - void getDisplayIdentificationData(); - void dumpHwc(); - void getDisplayedContentSamplingAttributes(HalDisplayId); - void getDeviceCompositionChanges(HalDisplayId); - void getHdrCapabilities(HalDisplayId); - void getDisplayedContentSample(HalDisplayId); - void getSupportedContentTypes(); - ui::Size getFuzzedSize(); - mat4 getFuzzedMatrix(); - - DisplayIdGenerator<HalVirtualDisplayId> mGenerator; - FuzzedDataProvider mFdp; - PhysicalDisplayId mPhysicalDisplayId; - android::impl::HWComposer mHwc{std::make_unique<Hwc2::mock::Composer>()}; -}; - -void DisplayHardwareFuzzer::validateDisplay(Hwc2::AidlComposer* composer, Display display) { - uint32_t outNumTypes, outNumRequests; - const auto frameIntervalRange = - mFdp.ConsumeIntegralInRange<int32_t>(Fps::fromValue(1).getPeriodNsecs(), - Fps::fromValue(120).getPeriodNsecs()); - composer->validateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), frameIntervalRange, - &outNumTypes, &outNumRequests); -} - -void DisplayHardwareFuzzer::presentOrValidateDisplay(Hwc2::AidlComposer* composer, - Display display) { - int32_t outPresentFence; - uint32_t outNumTypes, outNumRequests, state; - const auto frameIntervalRange = - mFdp.ConsumeIntegralInRange<int32_t>(Fps::fromValue(1).getPeriodNsecs(), - Fps::fromValue(120).getPeriodNsecs()); - composer->presentOrValidateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), frameIntervalRange, - &outNumTypes, &outNumRequests, &outPresentFence, &state); -} - -void DisplayHardwareFuzzer::setOutputBuffer(Hwc2::AidlComposer* composer, Display display) { - const native_handle_t buffer{}; - composer->setOutputBuffer(display, &buffer, mFdp.ConsumeIntegral<int32_t>() /*releaseFence*/); -} - -void DisplayHardwareFuzzer::setLayerSidebandStream(Hwc2::AidlComposer* composer, Display display, - Hwc2::V2_4::hal::Layer outLayer) { - const native_handle_t stream{}; - composer->setLayerSidebandStream(display, outLayer, &stream); -} - -Display DisplayHardwareFuzzer::createVirtualDisplay(Hwc2::AidlComposer* composer) { - namespace types = hardware::graphics::common; - using types::V1_2::PixelFormat; - PixelFormat format{}; - Display display; - composer->createVirtualDisplay(mFdp.ConsumeIntegral<uint32_t>() /*width*/, - mFdp.ConsumeIntegral<uint32_t>() /*height*/, &format, &display); - return display; -} - -void DisplayHardwareFuzzer::getDisplayVsyncPeriod() { - nsecs_t outVsyncPeriod; - mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId, &outVsyncPeriod); -} - -void DisplayHardwareFuzzer::setActiveModeWithConstraints() { - hal::VsyncPeriodChangeTimeline outTimeline; - mHwc.setActiveModeWithConstraints(mPhysicalDisplayId, kActiveConfig, {} /*constraints*/, - &outTimeline); -} - -void DisplayHardwareFuzzer::getDisplayIdentificationData() { - uint8_t outPort; - DisplayIdentificationData outData; - mHwc.getDisplayIdentificationData(kHwDisplayId, &outPort, &outData); -} - -void DisplayHardwareFuzzer::dumpHwc() { - std::string string = mFdp.ConsumeRandomLengthString().c_str(); - mHwc.dump(string); -} - -void DisplayHardwareFuzzer::getDeviceCompositionChanges(HalDisplayId halDisplayID) { - std::optional<impl::HWComposer::DeviceRequestedChanges> outChanges; - mHwc.getDeviceCompositionChanges(halDisplayID, - mFdp.ConsumeBool() /*frameUsesClientComposition*/, - std::chrono::steady_clock::now(), - mFdp.ConsumeIntegral<nsecs_t>(), - Fps::fromValue( - mFdp.ConsumeFloatingPointInRange<float>(1.f, 120.f)), - &outChanges); -} - -void DisplayHardwareFuzzer::getDisplayedContentSamplingAttributes(HalDisplayId halDisplayID) { - uint8_t outComponentMask; - ui::Dataspace dataSpace; - ui::PixelFormat pixelFormat; - mHwc.getDisplayedContentSamplingAttributes(halDisplayID, &pixelFormat, &dataSpace, - &outComponentMask); -} - -void DisplayHardwareFuzzer::getHdrCapabilities(HalDisplayId halDisplayID) { - HdrCapabilities outCapabilities; - mHwc.getHdrCapabilities(halDisplayID, &outCapabilities); -} - -void DisplayHardwareFuzzer::getDisplayedContentSample(HalDisplayId halDisplayID) { - DisplayedFrameStats outStats; - mHwc.getDisplayedContentSample(halDisplayID, mFdp.ConsumeIntegral<uint64_t>() /* maxFrames*/, - mFdp.ConsumeIntegral<uint64_t>() /*timestamps*/, &outStats); -} - -void DisplayHardwareFuzzer::getSupportedContentTypes() { - std::vector<hal::ContentType> contentType{}; - mHwc.getSupportedContentTypes(mPhysicalDisplayId, &contentType); -} - -void DisplayHardwareFuzzer::invokeAidlComposer() { - hardware::ProcessState::self()->startThreadPool(); - ProcessState::self()->startThreadPool(); - - if (!Hwc2::AidlComposer::isDeclared("default")) { - return; - } - - Hwc2::AidlComposer composer("default"); - - android::hardware::graphics::composer::hal::TestHWC2ComposerCallback composerCallback{}; - composer.registerCallback(composerCallback); - - Display display = createVirtualDisplay(&composer); - - composer.acceptDisplayChanges(display); - - Hwc2::V2_4::hal::Layer outLayer; - composer.createLayer(display, &outLayer); - - int32_t outPresentFence; - composer.presentDisplay(display, &outPresentFence); - - composer.setActiveConfig(display, Config{}); - - composer.setClientTarget(display, mFdp.ConsumeIntegral<uint32_t>(), sp<GraphicBuffer>(), - mFdp.ConsumeIntegral<int32_t>(), mFdp.PickValueInArray(kDataspaces), - {}, mFdp.ConsumeFloatingPoint<float>()); - - composer.setColorMode(display, mFdp.PickValueInArray(kColormodes), - mFdp.PickValueInArray(kRenderIntents)); - - setOutputBuffer(&composer, display); - - composer.setPowerMode(display, mFdp.PickValueInArray(kPowerModes)); - composer.setVsyncEnabled(display, mFdp.ConsumeBool() ? Vsync::ENABLE : Vsync::DISABLE); - - composer.setClientTargetSlotCount(display); - - validateDisplay(&composer, display); - - presentOrValidateDisplay(&composer, display); - - composer.setCursorPosition(display, outLayer, mFdp.ConsumeIntegral<uint8_t>() /*x*/, - mFdp.ConsumeIntegral<uint8_t>() /*y*/); - - composer.setLayerBuffer(display, outLayer, mFdp.ConsumeIntegral<uint32_t>() /*slot*/, - sp<GraphicBuffer>(), mFdp.ConsumeIntegral<int32_t>() /*acquireFence*/); - - composer.setLayerSurfaceDamage(display, outLayer, {} /*damage*/); - - composer.setLayerBlendMode(display, outLayer, mFdp.PickValueInArray(kBlendModes)); - - composer.setLayerColor(display, outLayer, - {mFdp.ConsumeFloatingPoint<float>() /*red*/, - mFdp.ConsumeFloatingPoint<float>() /*green*/, - mFdp.ConsumeFloatingPoint<float>() /*blue*/, - mFdp.ConsumeFloatingPoint<float>() /*alpha*/}); - composer.setLayerCompositionType(display, outLayer, mFdp.PickValueInArray(kCompositions)); - composer.setLayerDataspace(display, outLayer, mFdp.PickValueInArray(kDataspaces)); - composer.setLayerDisplayFrame(display, outLayer, {} /*frame*/); - composer.setLayerPlaneAlpha(display, outLayer, mFdp.ConsumeFloatingPoint<float>()); - - setLayerSidebandStream(&composer, display, outLayer); - - composer.setLayerSourceCrop(display, outLayer, {} /*crop*/); - - composer.setLayerTransform(display, outLayer, mFdp.PickValueInArray(kTransforms)); - - composer.setLayerVisibleRegion(display, outLayer, std::vector<IComposerClient::Rect>{}); - composer.setLayerZOrder(display, outLayer, mFdp.ConsumeIntegral<uint32_t>()); - - invokeComposerHal2_2(&composer, display, outLayer); - invokeComposerHal2_3(&composer, display, outLayer); - invokeComposerHal2_4(&composer, display, outLayer); - - composer.executeCommands(display); - - composer.destroyLayer(display, outLayer); - composer.destroyVirtualDisplay(display); -} - -void DisplayHardwareFuzzer::invokeComposerHal2_2(Hwc2::AidlComposer* composer, Display display, - Hwc2::V2_4::hal::Layer outLayer) { - const std::vector<PerFrameMetadata> perFrameMetadatas; - composer->setLayerPerFrameMetadata(display, outLayer, perFrameMetadatas); - - composer->getPerFrameMetadataKeys(display); - std::vector<RenderIntent> outRenderIntents; - - composer->getRenderIntents(display, mFdp.PickValueInArray(kColormodes), &outRenderIntents); - mat4 outMatrix; - composer->getDataspaceSaturationMatrix(mFdp.PickValueInArray(kDataspaces), &outMatrix); -} - -void DisplayHardwareFuzzer::invokeComposerHal2_3(Hwc2::AidlComposer* composer, Display display, - Hwc2::V2_4::hal::Layer outLayer) { - composer->setDisplayContentSamplingEnabled(display, mFdp.ConsumeBool() /*enabled*/, - mFdp.ConsumeIntegral<uint8_t>() /*componentMask*/, - mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/); - - DisplayedFrameStats outStats; - composer->getDisplayedContentSample(display, mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/, - mFdp.ConsumeIntegral<uint64_t>() /*timestamp*/, &outStats); - - composer->setLayerPerFrameMetadataBlobs(display, outLayer, std::vector<PerFrameMetadataBlob>{}); - - composer->setDisplayBrightness(display, mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), - Hwc2::Composer::DisplayBrightnessOptions{ - .applyImmediately = mFdp.ConsumeIntegral<bool>()}); -} - -void DisplayHardwareFuzzer::invokeComposerHal2_4(Hwc2::AidlComposer* composer, Display display, - Hwc2::V2_4::hal::Layer outLayer) { - VsyncPeriodChangeTimeline outTimeline; - composer->setActiveConfigWithConstraints(display, Config{}, - IComposerClient::VsyncPeriodChangeConstraints{}, - &outTimeline); - - composer->setAutoLowLatencyMode(display, mFdp.ConsumeBool()); - - composer->setContentType(display, mFdp.PickValueInArray(kContentTypes)); - - std::vector<uint8_t> value; - value.push_back(mFdp.ConsumeIntegral<uint8_t>()); - composer->setLayerGenericMetadata(display, outLayer, mFdp.ConsumeRandomLengthString() /*key*/, - mFdp.ConsumeBool() /*mandatory*/, value); -} - -ui::Size DisplayHardwareFuzzer::getFuzzedSize() { - ui::Size size{mFdp.ConsumeIntegral<int32_t>() /*width*/, - mFdp.ConsumeIntegral<int32_t>() /*height*/}; - return size; -} - -mat4 DisplayHardwareFuzzer::getFuzzedMatrix() { - mat4 matrix{mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()}; - return matrix; -} - -void DisplayHardwareFuzzer::setCursorPosition(HWC2::Layer* layer) { - layer->setCursorPosition(mFdp.ConsumeIntegral<int32_t>() /*x*/, - mFdp.ConsumeIntegral<int32_t>() /*y*/); -} - -void DisplayHardwareFuzzer::setBuffer(HWC2::Layer* layer) { - layer->setBuffer(mFdp.ConsumeIntegral<uint32_t>() /*slot*/, sp<GraphicBuffer>(), - sp<Fence>::make()); -} - -void DisplayHardwareFuzzer::setSurfaceDamage(HWC2::Layer* layer) { - Rect rhs{mFdp.ConsumeIntegral<uint32_t>() /*width*/, - mFdp.ConsumeIntegral<uint32_t>() /*height*/}; - const Region damage{rhs}; - layer->setSurfaceDamage(damage); -} - -void DisplayHardwareFuzzer::setVisibleRegion(HWC2::Layer* layer) { - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - Rect rect{width, height}; - const Region region{rect}; - layer->setVisibleRegion(region); -} - -void DisplayHardwareFuzzer::setDisplayFrame(HWC2::Layer* layer) { - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - const Rect frame{width, height}; - layer->setDisplayFrame(frame); -} - -void DisplayHardwareFuzzer::setLayerGenericMetadata(HWC2::Layer* layer) { - std::vector<uint8_t> value; - value.push_back(mFdp.ConsumeIntegral<uint8_t>()); - layer->setLayerGenericMetadata(mFdp.ConsumeRandomLengthString().c_str() /*name*/, - mFdp.ConsumeBool() /*mandatory*/, value); -} - -void DisplayHardwareFuzzer::setSidebandStream(HWC2::Layer* layer) { - const native_handle_t stream{}; - layer->setSidebandStream(&stream); -} - -void DisplayHardwareFuzzer::invokeLayer(HWC2::Layer* layer) { - setCursorPosition(layer); - setBuffer(layer); - setSurfaceDamage(layer); - - layer->setBlendMode(mFdp.PickValueInArray(kBlendModes)); - layer->setColor({mFdp.ConsumeFloatingPoint<float>() /*red*/, - mFdp.ConsumeFloatingPoint<float>() /*green*/, - mFdp.ConsumeFloatingPoint<float>() /*blue*/, - mFdp.ConsumeFloatingPoint<float>() /*alpha*/}); - layer->setCompositionType(mFdp.PickValueInArray(kCompositions)); - layer->setDataspace(mFdp.PickValueInArray(kDataspaces)); - - layer->setPerFrameMetadata(mFdp.ConsumeIntegral<int32_t>(), getFuzzedHdrMetadata(&mFdp)); - setDisplayFrame(layer); - - layer->setPlaneAlpha(mFdp.ConsumeFloatingPoint<float>()); - - setSidebandStream(layer); - - layer->setSourceCrop(getFuzzedFloatRect(&mFdp)); - layer->setTransform(mFdp.PickValueInArray(kTransforms)); - - setVisibleRegion(layer); - - layer->setZOrder(mFdp.ConsumeIntegral<uint32_t>()); - - layer->setColorTransform(getFuzzedMatrix()); - - setLayerGenericMetadata(layer); -} - -void DisplayHardwareFuzzer::invokeFrameBufferSurface() { - sp<IGraphicBufferProducer> bqProducer = sp<mock::GraphicBufferProducer>::make(); - sp<IGraphicBufferConsumer> bqConsumer; - BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); - - sp<FramebufferSurface> surface = - sp<FramebufferSurface>::make(mHwc, mPhysicalDisplayId, bqConsumer, - getFuzzedSize() /*size*/, getFuzzedSize() /*maxSize*/); - surface->beginFrame(mFdp.ConsumeBool()); - - surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes)); - surface->advanceFrame(mFdp.ConsumeFloatingPoint<float>()); - surface->onFrameCommitted(); - String8 result = String8(mFdp.ConsumeRandomLengthString().c_str()); - surface->dumpAsString(result); - surface->resizeBuffers(getFuzzedSize()); - surface->getClientTargetAcquireFence(); -} - -void DisplayHardwareFuzzer::invokeVirtualDisplaySurface() { - DisplayIdGenerator<HalVirtualDisplayId> mGenerator; - VirtualDisplayId VirtualDisplayId = mGenerator.generateId().value(); - - sp<SurfaceComposerClient> mClient = sp<SurfaceComposerClient>::make(); - sp<SurfaceControl> mSurfaceControl = - mClient->createSurface(String8("TestSurface"), 100, 100, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceBufferState, - /*parent*/ nullptr); - - auto mBlastBufferQueueAdapter = - sp<BLASTBufferQueue>::make("TestBLASTBufferQueue", mSurfaceControl, 100, 100, - PIXEL_FORMAT_RGBA_8888); - - sp<IGraphicBufferProducer> sink = mBlastBufferQueueAdapter->getIGraphicBufferProducer(); - sp<IGraphicBufferProducer> bqProducer = mBlastBufferQueueAdapter->getIGraphicBufferProducer(); - sp<IGraphicBufferConsumer> bqConsumer; - BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); - BufferQueue::createBufferQueue(&sink, &bqConsumer); - - auto surface = - sp<VirtualDisplaySurface>::make(mHwc, VirtualDisplayId, sink, bqProducer, bqConsumer, - mFdp.ConsumeRandomLengthString().c_str() /*name*/); - - surface->beginFrame(mFdp.ConsumeBool()); - surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes)); - surface->resizeBuffers(getFuzzedSize()); - surface->getClientTargetAcquireFence(); - surface->advanceFrame(mFdp.ConsumeFloatingPoint<float>()); - surface->onFrameCommitted(); - String8 result = String8(mFdp.ConsumeRandomLengthString().c_str()); - surface->dumpAsString(result); -} - -void DisplayHardwareFuzzer::invokeComposer() { - HalVirtualDisplayId halVirtualDisplayId = mGenerator.generateId().value(); - HalDisplayId halDisplayID = HalDisplayId{halVirtualDisplayId}; - - android::hardware::graphics::composer::hal::TestHWC2ComposerCallback composerCallback{}; - mHwc.setCallback(composerCallback); - - ui::PixelFormat pixelFormat{}; - if (!mHwc.allocateVirtualDisplay(halVirtualDisplayId, getFuzzedSize(), &pixelFormat)) { - return; - } - - getDisplayIdentificationData(); - - mHwc.hasDisplayCapability(halDisplayID, mFdp.PickValueInArray(kDisplayCapability)); - - mHwc.allocatePhysicalDisplay(kHwDisplayId, mPhysicalDisplayId); - - static auto hwcLayer = mHwc.createLayer(halDisplayID); - HWC2::Layer* layer = hwcLayer.get(); - invokeLayer(layer); - - getDeviceCompositionChanges(halDisplayID); - - mHwc.setClientTarget(halDisplayID, mFdp.ConsumeIntegral<uint32_t>(), Fence::NO_FENCE, - sp<GraphicBuffer>::make(), mFdp.PickValueInArray(kDataspaces), - mFdp.ConsumeFloatingPoint<float>()); - - mHwc.presentAndGetReleaseFences(halDisplayID, std::chrono::steady_clock::now()); - - mHwc.setPowerMode(mPhysicalDisplayId, mFdp.PickValueInArray(kPowerModes)); - - mHwc.setColorTransform(halDisplayID, getFuzzedMatrix()); - - mHwc.getPresentFence(halDisplayID); - - mHwc.getLayerReleaseFence(halDisplayID, layer); - - mHwc.setOutputBuffer(halVirtualDisplayId, sp<Fence>::make(), sp<GraphicBuffer>::make()); - - mHwc.clearReleaseFences(halDisplayID); - - getHdrCapabilities(halDisplayID); - - mHwc.getSupportedPerFrameMetadata(halDisplayID); - - mHwc.getRenderIntents(halDisplayID, ui::ColorMode()); - - mHwc.getDataspaceSaturationMatrix(halDisplayID, ui::Dataspace()); - - getDisplayedContentSamplingAttributes(halDisplayID); - - mHwc.setDisplayContentSamplingEnabled(halDisplayID, mFdp.ConsumeBool() /*enabled*/, - mFdp.ConsumeIntegral<uint8_t>() /*componentMask*/, - mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/); - - getDisplayedContentSample(halDisplayID); - - mHwc.setDisplayBrightness(mPhysicalDisplayId, mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), - Hwc2::Composer::DisplayBrightnessOptions{ - .applyImmediately = mFdp.ConsumeIntegral<bool>()}); - - mHwc.onHotplug(kHwDisplayId, hal::Connection::CONNECTED); - mHwc.updatesDeviceProductInfoOnHotplugReconnect(); - - mHwc.onVsync(kHwDisplayId, mFdp.ConsumeIntegral<int64_t>()); - mHwc.setVsyncEnabled(mPhysicalDisplayId, - mFdp.ConsumeBool() ? hal::Vsync::ENABLE : hal::Vsync::DISABLE); - - mHwc.isConnected(mPhysicalDisplayId); - mHwc.getModes(mPhysicalDisplayId, mFdp.ConsumeIntegral<int32_t>()); - mHwc.getActiveMode(mPhysicalDisplayId); - mHwc.getColorModes(mPhysicalDisplayId); - mHwc.hasCapability(mFdp.PickValueInArray(kCapability)); - - mHwc.setActiveColorMode(mPhysicalDisplayId, mFdp.PickValueInArray(kColormodes), - mFdp.PickValueInArray(kRenderIntents)); - - mHwc.getDisplayConnectionType(mPhysicalDisplayId); - mHwc.isVsyncPeriodSwitchSupported(mPhysicalDisplayId); - - getDisplayVsyncPeriod(); - - setActiveModeWithConstraints(); - - mHwc.setAutoLowLatencyMode(mPhysicalDisplayId, mFdp.ConsumeBool()); - - getSupportedContentTypes(); - - mHwc.setContentType(mPhysicalDisplayId, mFdp.PickValueInArray(kContentTypes)); - - dumpHwc(); - - mHwc.toPhysicalDisplayId(kHwDisplayId); - mHwc.fromPhysicalDisplayId(mPhysicalDisplayId); - mHwc.disconnectDisplay(halDisplayID); - - static hal::HWDisplayId displayId = mFdp.ConsumeIntegral<hal::HWDisplayId>(); - mHwc.onHotplug(displayId, - mFdp.ConsumeBool() ? hal::Connection::DISCONNECTED : hal::Connection::CONNECTED); -} - -template <size_t N> -DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) { - return DisplayIdentificationData(bytes, bytes + N - 1); -} - -void DisplayHardwareFuzzer::invokeDisplayIdentification() { - static const DisplayIdentificationData data = asDisplayIdentificationData(kInternalEdid); - isEdid(data); - parseEdid(data); - parseDisplayIdentificationData(mFdp.ConsumeIntegral<uint8_t>(), data); - getPnpId(getVirtualDisplayId(mFdp.ConsumeIntegral<uint32_t>())); - getPnpId(mFdp.ConsumeIntegral<uint8_t>()); -} - -void DisplayHardwareFuzzer::process() { - invokeComposer(); - invokeAidlComposer(); - invokeDisplayIdentification(); - invokeFrameBufferSurface(); - invokeVirtualDisplaySurface(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - DisplayHardwareFuzzer displayHardwareFuzzer(data, size); - displayHardwareFuzzer.process(); - return 0; -} - -} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h deleted file mode 100644 index b2dc20e216..0000000000 --- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2021 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 <utils/Condition.h> -#include <chrono> -#include <vector> - -#include <android/hardware/graphics/composer/2.4/IComposer.h> -#include <composer-hal/2.1/ComposerClient.h> -#include <composer-hal/2.2/ComposerClient.h> -#include <composer-hal/2.3/ComposerClient.h> -#include <composer-hal/2.4/ComposerClient.h> - -#include "DisplayHardware/HWC2.h" -#include "surfaceflinger_fuzzers_utils.h" - -namespace { -class LayerImpl; -class Frame; -class DelayedEventGenerator; -} // namespace - -namespace android { -class SurfaceComposerClient; -} // namespace android - -namespace android::hardware::graphics::composer::hal { - -using aidl::android::hardware::graphics::common::DisplayHotplugEvent; -using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::HWC2::ComposerCallback; - -class ComposerCallbackBridge : public IComposerCallback { -public: - ComposerCallbackBridge(ComposerCallback* callback, bool vsyncSwitchingSupported) - : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {} - - Return<void> onHotplug(HWDisplayId display, Connection connection) override { - const auto event = connection == Connection::CONNECTED ? DisplayHotplugEvent::CONNECTED - : DisplayHotplugEvent::DISCONNECTED; - mCallback->onComposerHalHotplugEvent(display, event); - return Void(); - } - - Return<void> onRefresh(HWDisplayId display) override { - mCallback->onComposerHalRefresh(display); - return Void(); - } - - Return<void> onVsync(HWDisplayId display, int64_t timestamp) override { - if (!mVsyncSwitchingSupported) { - mCallback->onComposerHalVsync(display, timestamp, std::nullopt); - } - return Void(); - } - - Return<void> onVsync_2_4(HWDisplayId display, int64_t timestamp, - VsyncPeriodNanos vsyncPeriodNanos) override { - if (mVsyncSwitchingSupported) { - mCallback->onComposerHalVsync(display, timestamp, vsyncPeriodNanos); - } - return Void(); - } - - Return<void> onVsyncPeriodTimingChanged(HWDisplayId display, - const VsyncPeriodChangeTimeline& timeline) override { - mCallback->onComposerHalVsyncPeriodTimingChanged(display, timeline); - return Void(); - } - - Return<void> onSeamlessPossible(HWDisplayId display) override { - mCallback->onComposerHalSeamlessPossible(display); - return Void(); - } - -private: - ComposerCallback* const mCallback; - const bool mVsyncSwitchingSupported; -}; - -struct TestHWC2ComposerCallback : public HWC2::ComposerCallback { - virtual ~TestHWC2ComposerCallback() = default; - void onComposerHalHotplugEvent(HWDisplayId, DisplayHotplugEvent) {} - void onComposerHalRefresh(HWDisplayId) {} - void onComposerHalVsync(HWDisplayId, int64_t, std::optional<VsyncPeriodNanos>) {} - void onComposerHalVsyncPeriodTimingChanged(HWDisplayId, const VsyncPeriodChangeTimeline&) {} - void onComposerHalSeamlessPossible(HWDisplayId) {} - void onComposerHalVsyncIdle(HWDisplayId) {} - void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) {} -}; - -} // namespace android::hardware::graphics::composer::hal diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp deleted file mode 100644 index ce8d47e71c..0000000000 --- a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2021 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 <FrameTracer/FrameTracer.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <perfetto/trace/trace.pb.h> - -namespace android::fuzz { - -using namespace google::protobuf; - -constexpr size_t kMaxStringSize = 256; -constexpr size_t kMinLayerIds = 1; -constexpr size_t kMaxLayerIds = 10; -constexpr int32_t kMinRange = 0; -constexpr int32_t kConfigDuration = 500; -constexpr int32_t kBufferSize = 1024; -constexpr int32_t kTimeOffset = 100000; - -class FrameTracerFuzzer { -public: - FrameTracerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) { - // Fuzzer is single-threaded, so no need to be thread-safe. - static bool wasInitialized = false; - if (!wasInitialized) { - perfetto::TracingInitArgs args; - args.backends = perfetto::kInProcessBackend; - perfetto::Tracing::Initialize(args); - wasInitialized = true; - } - mFrameTracer = std::make_unique<android::FrameTracer>(); - } - ~FrameTracerFuzzer() { mFrameTracer.reset(); } - void process(); - -private: - void traceTimestamp(); - void traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds); - void traceFence(std::vector<int32_t> layerIds, size_t numLayerIds); - std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest(); - std::unique_ptr<android::FrameTracer> mFrameTracer = nullptr; - std::vector<int32_t> generateLayerIds(size_t numLayerIds); - android::FenceToFenceTimeMap mFenceFactory; - FuzzedDataProvider mFdp; -}; - -std::unique_ptr<perfetto::TracingSession> FrameTracerFuzzer::getTracingSessionForTest() { - perfetto::TraceConfig cfg; - cfg.set_duration_ms(mFdp.ConsumeIntegralInRange<int32_t>(kMinRange, kConfigDuration)); - cfg.add_buffers()->set_size_kb(mFdp.ConsumeIntegralInRange<int32_t>(kMinRange, kBufferSize)); - auto* dsCfg = cfg.add_data_sources()->mutable_config(); - dsCfg->set_name(android::FrameTracer::kFrameTracerDataSource); - - auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend); - tracingSession->Setup(cfg); - return tracingSession; -} - -std::vector<int32_t> FrameTracerFuzzer::generateLayerIds(size_t numLayerIds) { - std::vector<int32_t> layerIds; - for (size_t i = 0; i < numLayerIds; ++i) { - layerIds.push_back(mFdp.ConsumeIntegral<int32_t>()); - } - return layerIds; -} - -void FrameTracerFuzzer::traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds) { - uint32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1)); - android::FrameTracer::FrameEvent::BufferEventType type = static_cast< - android::FrameTracer::FrameEvent::BufferEventType>( - mFdp.ConsumeIntegralInRange<uint32_t>(android::FrameTracer::FrameEvent::UNSPECIFIED, - android::FrameTracer::FrameEvent::CANCEL)); - mFrameTracer->traceTimestamp(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/, - mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/, - mFdp.ConsumeIntegral<nsecs_t>() /*timestamp*/, type, - mFdp.ConsumeIntegral<nsecs_t>() /*duration*/); -} - -void FrameTracerFuzzer::traceFence(std::vector<int32_t> layerIds, size_t numLayerIds) { - const nsecs_t signalTime = - mFdp.ConsumeBool() ? android::Fence::SIGNAL_TIME_PENDING : systemTime(); - const nsecs_t startTime = (signalTime == android::Fence::SIGNAL_TIME_PENDING) - ? signalTime - kTimeOffset - : signalTime + kTimeOffset; - auto fence = mFenceFactory.createFenceTimeForTest(android::Fence::NO_FENCE); - mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, signalTime); - int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1)); - mFrameTracer->traceFence(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/, - mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/, fence, - android::FrameTracer::FrameEvent::ACQUIRE_FENCE, startTime); -} - -void FrameTracerFuzzer::process() { - std::vector<int32_t> layerIds = - generateLayerIds(mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds)); - - std::unique_ptr<perfetto::TracingSession> tracingSession; - while (mFdp.remaining_bytes()) { - auto invokeFrametracerAPI = mFdp.PickValueInArray<const std::function<void()>>({ - [&]() { mFrameTracer->registerDataSource(); }, - [&]() { - if (tracingSession) { - tracingSession->StopBlocking(); - } - tracingSession = getTracingSessionForTest(); - tracingSession->StartBlocking(); - }, - [&]() { traceTimestamp(layerIds, layerIds.size()); }, - [&]() { traceFence(layerIds, layerIds.size()); }, - [&]() { - for (auto it = layerIds.begin(); it != layerIds.end(); ++it) { - mFrameTracer->traceNewLayer(*it /*layerId*/, - mFdp.ConsumeRandomLengthString( - kMaxStringSize) /*layerName*/); - } - }, - [&]() { mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, systemTime()); }, - }); - invokeFrametracerAPI(); - } - - for (auto it = layerIds.begin(); it != layerIds.end(); ++it) { - mFrameTracer->onDestroy(*it); - } -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - FrameTracerFuzzer frameTracerFuzzer(data, size); - frameTracerFuzzer.process(); - return 0; -} - -} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp deleted file mode 100644 index d13189e439..0000000000 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright 2021 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 <FuzzableDataspaces.h> -#include <binder/IServiceManager.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <ui/DisplayStatInfo.h> -#include "surfaceflinger_fuzzers_utils.h" - -namespace android::fuzz { - -static constexpr LatchUnsignaledConfig kLatchUnsignaledConfig[] = { - LatchUnsignaledConfig::AutoSingleLayer, - LatchUnsignaledConfig::Disabled, -}; - -static constexpr BnSurfaceComposer::ISurfaceComposerTag kSurfaceComposerTags[]{ - BnSurfaceComposer::BOOT_FINISHED, - BnSurfaceComposer::CREATE_CONNECTION, - BnSurfaceComposer::GET_STATIC_DISPLAY_INFO, - BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, - BnSurfaceComposer::CREATE_DISPLAY, - BnSurfaceComposer::DESTROY_DISPLAY, - BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, - BnSurfaceComposer::SET_TRANSACTION_STATE, - BnSurfaceComposer::AUTHENTICATE_SURFACE, - BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS, - BnSurfaceComposer::GET_DISPLAY_MODES, - BnSurfaceComposer::GET_ACTIVE_DISPLAY_MODE, - BnSurfaceComposer::GET_DISPLAY_STATE, - BnSurfaceComposer::CAPTURE_DISPLAY, - BnSurfaceComposer::CAPTURE_LAYERS, - BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, - BnSurfaceComposer::GET_ANIMATION_FRAME_STATS, - BnSurfaceComposer::SET_POWER_MODE, - BnSurfaceComposer::GET_DISPLAY_STATS, - BnSurfaceComposer::GET_HDR_CAPABILITIES, - BnSurfaceComposer::GET_DISPLAY_COLOR_MODES, - BnSurfaceComposer::GET_ACTIVE_COLOR_MODE, - BnSurfaceComposer::SET_ACTIVE_COLOR_MODE, - BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, - BnSurfaceComposer::INJECT_VSYNC, - BnSurfaceComposer::GET_LAYER_DEBUG_INFO, - BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, - BnSurfaceComposer::GET_COLOR_MANAGEMENT, - BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, - BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED, - BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, - BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, - BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY, - BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, - BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, - BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, - BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, - BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS, - BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS, - BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT, - BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS, - BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, - BnSurfaceComposer::NOTIFY_POWER_BOOST, - BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS, - BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, - BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE, - BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, - BnSurfaceComposer::SET_GAME_CONTENT_TYPE, - BnSurfaceComposer::SET_FRAME_RATE, - BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, - BnSurfaceComposer::SET_FRAME_TIMELINE_INFO, - BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, - BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY, - BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT, - BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO, - BnSurfaceComposer::ADD_FPS_LISTENER, - BnSurfaceComposer::REMOVE_FPS_LISTENER, - BnSurfaceComposer::OVERRIDE_HDR_TYPES, - BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER, - BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER, - BnSurfaceComposer::ON_PULL_ATOM, - BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER, - BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER, - BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER, - BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, - BnSurfaceComposer::GET_SCHEDULING_POLICY, -}; - -static constexpr uint32_t kMinCode = 1000; -static constexpr uint32_t kMaxCode = 1050; - -class SurfaceFlingerFuzzer { -public: - SurfaceFlingerFuzzer(const uint8_t *data, size_t size) : mFdp(data, size) { - mFlinger = sp<SurfaceFlinger>::fromExisting(mTestableFlinger.flinger()); - }; - void process(const uint8_t *data, size_t size); - -private: - void setUp(); - void invokeFlinger(); - void setTransactionState(); - void setInternalDisplayPrimaries(); - void setDisplayStateLocked(); - void onTransact(const uint8_t *data, size_t size); - - FuzzedDataProvider mFdp; - TestableSurfaceFlinger mTestableFlinger; - sp<SurfaceFlinger> mFlinger = nullptr; -}; - -void SurfaceFlingerFuzzer::invokeFlinger() { - mFlinger->setSchedFifo(mFdp.ConsumeBool()); - mFlinger->setSchedAttr(mFdp.ConsumeBool()); - mFlinger->getServiceName(); - mFlinger->hasSyncFramework = mFdp.ConsumeBool(); - mFlinger->dispSyncPresentTimeOffset = mFdp.ConsumeIntegral<int64_t>(); - mFlinger->useHwcForRgbToYuv = mFdp.ConsumeBool(); - mFlinger->maxFrameBufferAcquiredBuffers = mFdp.ConsumeIntegral<int64_t>(); - mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>(); - mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>(); - mTestableFlinger.mutableSupportsWideColor() = mFdp.ConsumeBool(); - mFlinger->useContextPriority = mFdp.ConsumeBool(); - - mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces); - mFlinger->defaultCompositionPixelFormat = mFdp.PickValueInArray(kPixelFormats); - mFlinger->wideColorGamutCompositionDataspace = mFdp.PickValueInArray(kDataspaces); - mFlinger->wideColorGamutCompositionPixelFormat = mFdp.PickValueInArray(kPixelFormats); - - mFlinger->enableLatchUnsignaledConfig = mFdp.PickValueInArray(kLatchUnsignaledConfig); - - using FrameHint = SurfaceFlinger::FrameHint; - mFlinger->scheduleComposite(mFdp.ConsumeBool() ? FrameHint::kActive : FrameHint::kNone); - mFlinger->scheduleRepaint(); - mFlinger->scheduleSample(); - - sp<IBinder> handle = defaultServiceManager()->checkService( - String16(mFdp.ConsumeRandomLengthString().c_str())); - LayerHandle::getLayer(handle); - mFlinger->disableExpensiveRendering(); -} - -void SurfaceFlingerFuzzer::setInternalDisplayPrimaries() { - ui::DisplayPrimaries primaries; - primaries.red.X = mFdp.ConsumeFloatingPoint<float>(); - primaries.red.Y = mFdp.ConsumeFloatingPoint<float>(); - primaries.red.Z = mFdp.ConsumeFloatingPoint<float>(); - primaries.green.X = mFdp.ConsumeFloatingPoint<float>(); - primaries.green.Y = mFdp.ConsumeFloatingPoint<float>(); - primaries.green.Z = mFdp.ConsumeFloatingPoint<float>(); - primaries.blue.X = mFdp.ConsumeFloatingPoint<float>(); - primaries.blue.Y = mFdp.ConsumeFloatingPoint<float>(); - primaries.blue.Z = mFdp.ConsumeFloatingPoint<float>(); - primaries.white.X = mFdp.ConsumeFloatingPoint<float>(); - primaries.white.Y = mFdp.ConsumeFloatingPoint<float>(); - primaries.white.Z = mFdp.ConsumeFloatingPoint<float>(); - mTestableFlinger.setInternalDisplayPrimaries(primaries); -} - -void SurfaceFlingerFuzzer::setTransactionState() { - Vector<ComposerState> states; - Vector<DisplayState> displays; - ComposerState composerState; - composerState.state.what = layer_state_t::eLayerChanged; - composerState.state.surface = nullptr; - states.add(composerState); - uint32_t flags = mFdp.ConsumeIntegral<uint32_t>(); - const sp<IBinder> applyToken = nullptr; - int64_t desiredPresentTime = mFdp.ConsumeIntegral<int64_t>(); - bool isAutoTimestamp = mFdp.ConsumeBool(); - bool hasListenerCallbacks = mFdp.ConsumeBool(); - std::vector<ListenerCallbacks> listenerCallbacks{}; - uint64_t transactionId = mFdp.ConsumeIntegral<uint64_t>(); - std::vector<uint64_t> mergedTransactionIds{}; - - mTestableFlinger.setTransactionState(FrameTimelineInfo{}, states, displays, flags, applyToken, - InputWindowCommands{}, desiredPresentTime, isAutoTimestamp, - {}, hasListenerCallbacks, listenerCallbacks, transactionId, - mergedTransactionIds); -} - -void SurfaceFlingerFuzzer::setDisplayStateLocked() { - DisplayState state{}; - mTestableFlinger.setDisplayStateLocked(state); -} - -void SurfaceFlingerFuzzer::onTransact(const uint8_t *data, size_t size) { - Parcel fuzzedData, reply; - fuzzedData.writeInterfaceToken(String16("android.ui.ISurfaceComposer")); - fuzzedData.setData(data, size); - fuzzedData.setDataPosition(0); - uint32_t code = mFdp.ConsumeBool() ? mFdp.PickValueInArray(kSurfaceComposerTags) - : mFdp.ConsumeIntegralInRange<uint32_t>(kMinCode, kMaxCode); - mTestableFlinger.onTransact(code, fuzzedData, &reply, 0); -} - -void SurfaceFlingerFuzzer::setUp() { - mTestableFlinger.setupScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_unique<android::mock::VSyncTracker>(), - std::make_unique<android::mock::EventThread>(), - std::make_unique<android::mock::EventThread>()); - - mTestableFlinger.setupTimeStats(std::make_unique<android::mock::TimeStats>()); - - std::unique_ptr<android::renderengine::RenderEngine> renderEngine = - std::make_unique<android::renderengine::mock::RenderEngine>(); - mTestableFlinger.setupRenderEngine(std::move(renderEngine)); - mTestableFlinger.setupComposer(std::make_unique<android::Hwc2::mock::Composer>()); -} - -void SurfaceFlingerFuzzer::process(const uint8_t *data, size_t size) { - setUp(); - - invokeFlinger(); - - mTestableFlinger.fuzzSurfaceFlinger(data, size); - - mTestableFlinger.setCreateBufferQueueFunction( - surfaceflinger::test::Factory::CreateBufferQueueFunction()); - mTestableFlinger.setCreateNativeWindowSurface( - surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction()); - - setInternalDisplayPrimaries(); - - mTestableFlinger.enableHalVirtualDisplays(mFdp.ConsumeBool()); - - FTL_FAKE_GUARD(kMainThreadContext, - mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>())); - - mTestableFlinger.notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>()); - - setDisplayStateLocked(); - - setTransactionState(); - mTestableFlinger.flushTransactionQueues(); - - onTransact(data, size); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - android::fuzz::SurfaceFlingerFuzzer surfaceFlingerFuzzer(data, size); - surfaceFlingerFuzzer.process(data, size); - return 0; -} - -} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h deleted file mode 100644 index 81fbc847a3..0000000000 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ /dev/null @@ -1,818 +0,0 @@ -/* - * Copyright 2021 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 <compositionengine/Display.h> -#include <compositionengine/LayerFECompositionState.h> -#include <compositionengine/OutputLayer.h> -#include <compositionengine/impl/CompositionEngine.h> -#include <compositionengine/impl/Display.h> -#include <compositionengine/impl/OutputLayerCompositionState.h> -#include <ftl/fake_guard.h> -#include <gui/LayerDebugInfo.h> -#include <gui/ScreenCaptureResults.h> -#include <gui/SurfaceComposerClient.h> -#include <gui/mock/GraphicBufferProducer.h> -#include <ui/DisplayStatInfo.h> -#include <ui/DynamicDisplayInfo.h> - -#include "DisplayDevice.h" -#include "DisplayHardware/ComposerHal.h" -#include "FrameTimeline/FrameTimeline.h" -#include "FrameTracer/FrameTracer.h" -#include "FrontEnd/LayerHandle.h" -#include "Layer.h" -#include "NativeWindowSurface.h" -#include "Scheduler/EventThread.h" -#include "Scheduler/MessageQueue.h" -#include "Scheduler/RefreshRateSelector.h" -#include "Scheduler/VSyncTracker.h" -#include "Scheduler/VsyncConfiguration.h" -#include "Scheduler/VsyncController.h" -#include "Scheduler/VsyncModulator.h" -#include "StartPropertySetThread.h" -#include "SurfaceFlinger.h" -#include "SurfaceFlingerDefaultFactory.h" -#include "ThreadContext.h" -#include "TimeStats/TimeStats.h" -#include "surfaceflinger_scheduler_fuzzer.h" - -#include "renderengine/mock/RenderEngine.h" -#include "scheduler/TimeKeeper.h" -#include "tests/unittests/mock/DisplayHardware/MockComposer.h" -#include "tests/unittests/mock/DisplayHardware/MockDisplayMode.h" -#include "tests/unittests/mock/DisplayHardware/MockHWC2.h" -#include "tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h" -#include "tests/unittests/mock/MockEventThread.h" -#include "tests/unittests/mock/MockFrameTimeline.h" -#include "tests/unittests/mock/MockFrameTracer.h" -#include "tests/unittests/mock/MockNativeWindowSurface.h" -#include "tests/unittests/mock/MockTimeStats.h" -#include "tests/unittests/mock/MockVSyncTracker.h" -#include "tests/unittests/mock/MockVsyncController.h" - -namespace android { -namespace Hwc2 { - -class Composer; - -namespace types = hardware::graphics::common; - -namespace V2_1 = hardware::graphics::composer::V2_1; -namespace V2_2 = hardware::graphics::composer::V2_2; -namespace V2_3 = hardware::graphics::composer::V2_3; -namespace V2_4 = hardware::graphics::composer::V2_4; - -using types::V1_0::ColorTransform; -using types::V1_0::Transform; -using types::V1_1::RenderIntent; -using types::V1_2::ColorMode; -using types::V1_2::Dataspace; -using types::V1_2::PixelFormat; - -using V2_1::Config; -using V2_1::Display; -using V2_1::Error; -using V2_1::Layer; -using V2_4::CommandReaderBase; -using V2_4::CommandWriterBase; -using V2_4::IComposer; -using V2_4::IComposerCallback; -using V2_4::IComposerClient; -using V2_4::VsyncPeriodChangeTimeline; -using V2_4::VsyncPeriodNanos; -using DisplayCapability = IComposerClient::DisplayCapability; -using PerFrameMetadata = IComposerClient::PerFrameMetadata; -using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey; -using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob; -}; // namespace Hwc2 - -static constexpr hal::HWDisplayId kHwDisplayId = 1000; - -static constexpr ui::Hdr kHdrTypes[] = {ui::Hdr::DOLBY_VISION, ui::Hdr::HDR10, ui::Hdr::HLG, - ui::Hdr::HDR10_PLUS}; - -static constexpr ui::ColorMode kColormodes[] = {ui::ColorMode::NATIVE, - ui::ColorMode::STANDARD_BT601_625, - ui::ColorMode::STANDARD_BT601_625_UNADJUSTED, - ui::ColorMode::STANDARD_BT601_525, - ui::ColorMode::STANDARD_BT601_525_UNADJUSTED, - ui::ColorMode::STANDARD_BT709, - ui::ColorMode::DCI_P3, - ui::ColorMode::SRGB, - ui::ColorMode::ADOBE_RGB, - ui::ColorMode::DISPLAY_P3, - ui::ColorMode::BT2020, - ui::ColorMode::BT2100_PQ, - ui::ColorMode::BT2100_HLG, - ui::ColorMode::DISPLAY_BT2020}; - -static constexpr ui::PixelFormat kPixelFormats[] = {ui::PixelFormat::RGBA_8888, - ui::PixelFormat::RGBX_8888, - ui::PixelFormat::RGB_888, - ui::PixelFormat::RGB_565, - ui::PixelFormat::BGRA_8888, - ui::PixelFormat::YCBCR_422_SP, - ui::PixelFormat::YCRCB_420_SP, - ui::PixelFormat::YCBCR_422_I, - ui::PixelFormat::RGBA_FP16, - ui::PixelFormat::RAW16, - ui::PixelFormat::BLOB, - ui::PixelFormat::IMPLEMENTATION_DEFINED, - ui::PixelFormat::YCBCR_420_888, - ui::PixelFormat::RAW_OPAQUE, - ui::PixelFormat::RAW10, - ui::PixelFormat::RAW12, - ui::PixelFormat::RGBA_1010102, - ui::PixelFormat::Y8, - ui::PixelFormat::Y16, - ui::PixelFormat::YV12, - ui::PixelFormat::DEPTH_16, - ui::PixelFormat::DEPTH_24, - ui::PixelFormat::DEPTH_24_STENCIL_8, - ui::PixelFormat::DEPTH_32F, - ui::PixelFormat::DEPTH_32F_STENCIL_8, - ui::PixelFormat::STENCIL_8, - ui::PixelFormat::YCBCR_P010, - ui::PixelFormat::HSV_888}; - -inline VsyncId getFuzzedVsyncId(FuzzedDataProvider& fdp) { - return VsyncId{fdp.ConsumeIntegral<int64_t>()}; -} - -inline TimePoint getFuzzedTimePoint(FuzzedDataProvider& fdp) { - return TimePoint::fromNs(fdp.ConsumeIntegral<nsecs_t>()); -} - -inline Duration getFuzzedDuration(FuzzedDataProvider& fdp) { - return Duration::fromNs(fdp.ConsumeIntegral<nsecs_t>()); -} - -inline FloatRect getFuzzedFloatRect(FuzzedDataProvider* fdp) { - return FloatRect(fdp->ConsumeFloatingPoint<float>() /*left*/, - fdp->ConsumeFloatingPoint<float>() /*right*/, - fdp->ConsumeFloatingPoint<float>() /*top*/, - fdp->ConsumeFloatingPoint<float>() /*bottom*/); -} - -inline HdrMetadata getFuzzedHdrMetadata(FuzzedDataProvider* fdp) { - HdrMetadata hdrMetadata; - if (fdp->ConsumeBool()) { - hdrMetadata.cta8613.maxContentLightLevel = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.cta8613.maxFrameAverageLightLevel = fdp->ConsumeFloatingPoint<float>(); - - hdrMetadata.validTypes |= HdrMetadata::CTA861_3; - } else { - hdrMetadata.smpte2086.displayPrimaryRed.x = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.displayPrimaryRed.y = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.displayPrimaryGreen.x = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.displayPrimaryGreen.y = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.displayPrimaryBlue.x = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.displayPrimaryBlue.y = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.whitePoint.x = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.whitePoint.y = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.minLuminance = fdp->ConsumeFloatingPoint<float>(); - hdrMetadata.smpte2086.maxLuminance = fdp->ConsumeFloatingPoint<float>(); - - hdrMetadata.validTypes |= HdrMetadata::SMPTE2086; - } - return hdrMetadata; -} - -class EventThread; - -namespace hal = android::hardware::graphics::composer::hal; - -struct FakePhaseOffsets : scheduler::VsyncConfiguration { - static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0; - static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0); - - scheduler::VsyncConfigSet getConfigsForRefreshRate(Fps) const override { - return getCurrentConfigs(); - } - - scheduler::VsyncConfigSet getCurrentConfigs() const override { - return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, - FAKE_DURATION_OFFSET_NS}, - {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, - FAKE_DURATION_OFFSET_NS}, - {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, - FAKE_DURATION_OFFSET_NS}, - FAKE_DURATION_OFFSET_NS}; - } - - void reset() override {} - void setRefreshRateFps(Fps) override {} - void dump(std::string &) const override {} -}; - -namespace scheduler { - -class TestableScheduler : public Scheduler, private ICompositor { -public: - TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback) - : TestableScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_shared<android::mock::VSyncTracker>(), selectorPtr, - std::move(modulatorPtr), callback, vsyncTrackerCallback) {} - - TestableScheduler(std::unique_ptr<VsyncController> controller, - VsyncSchedule::TrackerPtr tracker, - std::shared_ptr<RefreshRateSelector> selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback) - : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr), - vsyncTrackerCallback) { - const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); - registerDisplayInternal(displayId, std::move(selectorPtr), - std::shared_ptr<VsyncSchedule>( - new VsyncSchedule(displayId, std::move(tracker), - std::make_shared<FuzzImplVSyncDispatch>(), - std::move(controller)))); - } - - ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { - return Scheduler::createConnection(std::move(eventThread)); - } - - auto &mutableLayerHistory() { return mLayerHistory; } - - auto refreshRateSelector() { return pacesetterSelectorPtr(); } - - void replaceTouchTimer(int64_t millis) { - if (mTouchTimer) { - mTouchTimer.reset(); - } - mTouchTimer.emplace( - "Testable Touch timer", std::chrono::milliseconds(millis), - [this] { touchTimerCallback(TimerState::Reset); }, - [this] { touchTimerCallback(TimerState::Expired); }); - mTouchTimer->start(); - } - - bool isTouchActive() { - std::lock_guard<std::mutex> lock(mPolicyLock); - return mPolicy.touch == Scheduler::TouchState::Active; - } - - void dispatchCachedReportedMode() { - std::lock_guard<std::mutex> lock(mPolicyLock); - return Scheduler::dispatchCachedReportedMode(); - } - - void clearCachedReportedMode() { - std::lock_guard<std::mutex> lock(mPolicyLock); - mPolicy.cachedModeChangedParams.reset(); - } - - void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode &mode) { - return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); - } - - using Scheduler::setVsyncConfig; - -private: - // ICompositor overrides: - void configure() override {} - bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { return false; } - CompositeResultsPerDisplay composite(PhysicalDisplayId, - const scheduler::FrameTargeters&) override { - return {}; - } - void sample() override {} - - // MessageQueue overrides: - void scheduleFrame() override {} - void postMessage(sp<MessageHandler>&& handler) override { handler->handleMessage(Message()); } -}; - -} // namespace scheduler - -namespace surfaceflinger::test { - -class Factory final : public surfaceflinger::Factory { - struct NoOpMessageQueue : android::impl::MessageQueue { - using android::impl::MessageQueue::MessageQueue; - void onFrameSignal(ICompositor&, VsyncId, TimePoint) override {} - }; - -public: - ~Factory() = default; - - std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; } - - std::unique_ptr<MessageQueue> createMessageQueue(ICompositor& compositor) { - return std::make_unique<NoOpMessageQueue>(compositor); - } - - std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( - Fps /*currentRefreshRate*/) override { - return std::make_unique<FakePhaseOffsets>(); - } - - std::unique_ptr<scheduler::Scheduler> createScheduler( - const std::shared_ptr<scheduler::RefreshRateSelector>&, - scheduler::ISchedulerCallback&) { - return nullptr; - } - - sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override { - return sp<StartPropertySetThread>::make(timestampPropertyValue); - } - - sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs &creationArgs) override { - return sp<DisplayDevice>::make(creationArgs); - } - - sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, - uint32_t layerCount, uint64_t usage, - std::string requestorName) override { - return sp<GraphicBuffer>::make(width, height, format, layerCount, usage, requestorName); - } - - void createBufferQueue(sp<IGraphicBufferProducer> *outProducer, - sp<IGraphicBufferConsumer> *outConsumer, - bool consumerIsSurfaceFlinger) override { - if (!mCreateBufferQueue) { - BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); - return; - } - mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); - } - - std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( - const sp<IGraphicBufferProducer> &producer) override { - if (!mCreateNativeWindowSurface) return nullptr; - return mCreateNativeWindowSurface(producer); - } - - std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override { - return compositionengine::impl::createCompositionEngine(); - } - - sp<Layer> createBufferStateLayer(const LayerCreationArgs &) override { return nullptr; } - - sp<Layer> createEffectLayer(const LayerCreationArgs &args) override { - return sp<Layer>::make(args); - } - - sp<LayerFE> createLayerFE(const std::string &layerName) override { - return sp<LayerFE>::make(layerName); - } - - std::unique_ptr<FrameTracer> createFrameTracer() override { - return std::make_unique<android::mock::FrameTracer>(); - } - - std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline( - std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override { - return std::make_unique<android::mock::FrameTimeline>(timeStats, surfaceFlingerPid); - } - - using CreateBufferQueueFunction = - std::function<void(sp<IGraphicBufferProducer> * /* outProducer */, - sp<IGraphicBufferConsumer> * /* outConsumer */, - bool /* consumerIsSurfaceFlinger */)>; - CreateBufferQueueFunction mCreateBufferQueue; - - using CreateNativeWindowSurfaceFunction = - std::function<std::unique_ptr<surfaceflinger::NativeWindowSurface>( - const sp<IGraphicBufferProducer> &)>; - CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface; - - using CreateCompositionEngineFunction = - std::function<std::unique_ptr<compositionengine::CompositionEngine>()>; - CreateCompositionEngineFunction mCreateCompositionEngine; -}; - -} // namespace surfaceflinger::test - -// TODO(b/189053744) : Create a common test/mock library for surfaceflinger -class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback, - private scheduler::IVsyncTrackerCallback { -public: - using HotplugEvent = SurfaceFlinger::HotplugEvent; - - SurfaceFlinger *flinger() { return mFlinger.get(); } - scheduler::TestableScheduler *scheduler() { return mScheduler; } - - void initializeDisplays() { - FTL_FAKE_GUARD(kMainThreadContext, mFlinger->initializeDisplays()); - } - - void setGlobalShadowSettings(FuzzedDataProvider *fdp) { - const half4 ambientColor{fdp->ConsumeFloatingPoint<float>(), - fdp->ConsumeFloatingPoint<float>(), - fdp->ConsumeFloatingPoint<float>(), - fdp->ConsumeFloatingPoint<float>()}; - const half4 spotColor{fdp->ConsumeFloatingPoint<float>(), - fdp->ConsumeFloatingPoint<float>(), - fdp->ConsumeFloatingPoint<float>(), - fdp->ConsumeFloatingPoint<float>()}; - float lightPosY = fdp->ConsumeFloatingPoint<float>(); - float lightPosZ = fdp->ConsumeFloatingPoint<float>(); - float lightRadius = fdp->ConsumeFloatingPoint<float>(); - mFlinger->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, - lightRadius); - } - - void onPullAtom(FuzzedDataProvider *fdp) { - const int32_t atomId = fdp->ConsumeIntegral<uint8_t>(); - std::vector<uint8_t> pulledData = fdp->ConsumeRemainingBytes<uint8_t>(); - bool success = fdp->ConsumeBool(); - mFlinger->onPullAtom(atomId, &pulledData, &success); - } - - void fuzzDumpsysAndDebug(FuzzedDataProvider *fdp) { - std::string result = fdp->ConsumeRandomLengthString().c_str(); - mFlinger->appendSfConfigString(result); - result = fdp->ConsumeRandomLengthString().c_str(); - mFlinger->listLayersLocked(result); - - using DumpArgs = Vector<String16>; - DumpArgs dumpArgs; - dumpArgs.push_back(String16(fdp->ConsumeRandomLengthString().c_str())); - mFlinger->clearStatsLocked(dumpArgs, result); - - mFlinger->dumpTimeStats(dumpArgs, fdp->ConsumeBool(), result); - FTL_FAKE_GUARD(kMainThreadContext, - mFlinger->logFrameStats(TimePoint::fromNs(fdp->ConsumeIntegral<nsecs_t>()))); - - result = fdp->ConsumeRandomLengthString().c_str(); - mFlinger->dumpFrameTimeline(dumpArgs, result); - - result = fdp->ConsumeRandomLengthString().c_str(); - mFlinger->dumpRawDisplayIdentificationData(dumpArgs, result); - - perfetto::protos::LayersProto layersProto = - mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>()); - mFlinger->dumpOffscreenLayersProto(layersProto); - mFlinger->dumpDisplayProto(); - - result = fdp->ConsumeRandomLengthString().c_str(); - mFlinger->dumpHwc(result); - - mFlinger->calculateColorMatrix(fdp->ConsumeFloatingPoint<float>()); - mFlinger->updateColorMatrixLocked(); - mFlinger->CheckTransactCodeCredentials(fdp->ConsumeIntegral<uint32_t>()); - } - - void getCompositionPreference() { - ui::Dataspace outDataspace; - ui::PixelFormat outPixelFormat; - ui::Dataspace outWideColorGamutDataspace; - ui::PixelFormat outWideColorGamutPixelFormat; - mFlinger->getCompositionPreference(&outDataspace, &outPixelFormat, - &outWideColorGamutDataspace, - &outWideColorGamutPixelFormat); - } - - void overrideHdrTypes(const sp<IBinder>& display, FuzzedDataProvider* fdp) { - std::vector<ui::Hdr> hdrTypes; - hdrTypes.push_back(fdp->PickValueInArray(kHdrTypes)); - mFlinger->overrideHdrTypes(display, hdrTypes); - } - - void getDisplayedContentSample(const sp<IBinder>& display, FuzzedDataProvider* fdp) { - DisplayedFrameStats outDisplayedFrameStats; - mFlinger->getDisplayedContentSample(display, fdp->ConsumeIntegral<uint64_t>(), - fdp->ConsumeIntegral<uint64_t>(), - &outDisplayedFrameStats); - } - - void getDisplayStats(const sp<IBinder>& display) { - android::DisplayStatInfo stats; - mFlinger->getDisplayStats(display, &stats); - } - - void getDisplayState(const sp<IBinder>& display) { - ui::DisplayState displayState; - mFlinger->getDisplayState(display, &displayState); - } - - void getStaticDisplayInfo(int64_t displayId) { - ui::StaticDisplayInfo staticDisplayInfo; - mFlinger->getStaticDisplayInfo(displayId, &staticDisplayInfo); - } - - void getDynamicDisplayInfo(int64_t displayId) { - android::ui::DynamicDisplayInfo dynamicDisplayInfo; - mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo); - } - void getDisplayNativePrimaries(const sp<IBinder>& display) { - android::ui::DisplayPrimaries displayPrimaries; - mFlinger->getDisplayNativePrimaries(display, displayPrimaries); - } - - void getDesiredDisplayModeSpecs(const sp<IBinder>& display) { - gui::DisplayModeSpecs _; - mFlinger->getDesiredDisplayModeSpecs(display, &_); - } - - // TODO(b/248317436): extend to cover all displays for multi-display devices - static std::optional<PhysicalDisplayId> getFirstDisplayId() { - std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds(); - if (ids.empty()) return {}; - return ids.front(); - } - - std::pair<sp<IBinder>, PhysicalDisplayId> fuzzBoot(FuzzedDataProvider* fdp) { - mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool()); - const sp<Client> client = sp<Client>::make(mFlinger); - - DisplayIdGenerator<HalVirtualDisplayId> kGenerator; - HalVirtualDisplayId halVirtualDisplayId = kGenerator.generateId().value(); - - ui::Size uiSize{fdp->ConsumeIntegral<int32_t>(), fdp->ConsumeIntegral<int32_t>()}; - ui::PixelFormat pixelFormat{}; - mFlinger->getHwComposer().allocateVirtualDisplay(halVirtualDisplayId, uiSize, &pixelFormat); - - PhysicalDisplayId physicalDisplayId = getFirstDisplayId().value_or( - PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>())); - mFlinger->getHwComposer().allocatePhysicalDisplay(kHwDisplayId, physicalDisplayId); - - sp<IBinder> display = - mFlinger->createDisplay(String8(fdp->ConsumeRandomLengthString().c_str()), - fdp->ConsumeBool()); - - initializeDisplays(); - mFlinger->getPhysicalDisplayToken(physicalDisplayId); - - mFlinger->mStartPropertySetThread = - mFlinger->getFactory().createStartPropertySetThread(fdp->ConsumeBool()); - - mFlinger->bootFinished(); - - return {display, physicalDisplayId}; - } - - void fuzzSurfaceFlinger(const uint8_t *data, size_t size) { - FuzzedDataProvider mFdp(data, size); - - const auto [display, displayId] = fuzzBoot(&mFdp); - - sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make(); - - mFlinger->createDisplayEventConnection(); - - getDisplayStats(display); - getDisplayState(display); - getStaticDisplayInfo(displayId.value); - getDynamicDisplayInfo(displayId.value); - getDisplayNativePrimaries(display); - - mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool()); - mFlinger->setGameContentType(display, mFdp.ConsumeBool()); - mFlinger->setPowerMode(display, mFdp.ConsumeIntegral<int>()); - - overrideHdrTypes(display, &mFdp); - - onPullAtom(&mFdp); - - getCompositionPreference(); - getDisplayedContentSample(display, &mFdp); - getDesiredDisplayModeSpecs(display); - - bool outSupport; - mFlinger->getDisplayBrightnessSupport(display, &outSupport); - - mFlinger->notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>()); - - setGlobalShadowSettings(&mFdp); - - mFlinger->binderDied(display); - mFlinger->onFirstRef(); - - mFlinger->updateInputFlinger(VsyncId{}, TimePoint{}); - mFlinger->updateCursorAsync(); - - mutableScheduler().setVsyncConfig({.sfOffset = mFdp.ConsumeIntegral<nsecs_t>(), - .appOffset = mFdp.ConsumeIntegral<nsecs_t>(), - .sfWorkDuration = getFuzzedDuration(mFdp), - .appWorkDuration = getFuzzedDuration(mFdp)}, - getFuzzedDuration(mFdp)); - - { - ftl::FakeGuard guard(kMainThreadContext); - - mFlinger->commitTransactionsLegacy(); - mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)); - - scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool()); - mFlinger->onCompositionPresented(displayId, ftl::init::map(displayId, &frameTargeter), - mFdp.ConsumeIntegral<nsecs_t>()); - } - - mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); - mFlinger->clearTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); - mFlinger->commitOffscreenLayers(); - - mFlinger->frameIsEarly(getFuzzedTimePoint(mFdp), getFuzzedVsyncId(mFdp)); - mFlinger->computeLayerBounds(); - mFlinger->startBootAnim(); - - mFlinger->readPersistentProperties(); - - mFlinger->exceedsMaxRenderTargetSize(mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); - - mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>()); - - mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool()); - - fuzzDumpsysAndDebug(&mFdp); - - mFlinger->destroyDisplay(display); - } - - void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { - mFlinger->mRenderEngine = std::move(renderEngine); - mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); - } - - void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { - mFlinger->mCompositionEngine->setHwComposer( - std::make_unique<impl::HWComposer>(std::move(composer))); - } - - void setupTimeStats(const std::shared_ptr<TimeStats> &timeStats) { - mFlinger->mCompositionEngine->setTimeStats(timeStats); - } - - // The ISchedulerCallback argument can be nullptr for a no-op implementation. - void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, - std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, - std::unique_ptr<EventThread> appEventThread, - std::unique_ptr<EventThread> sfEventThread, - scheduler::ISchedulerCallback* callback = nullptr, - scheduler::IVsyncTrackerCallback* vsyncTrackerCallback = nullptr, - bool hasMultipleModes = false) { - constexpr DisplayModeId kModeId60{0}; - DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz)); - - if (hasMultipleModes) { - constexpr DisplayModeId kModeId90{1}; - modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz)); - } - - mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); - const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getVsyncRate(); - mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); - - mFlinger->mRefreshRateStats = - std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps, - hal::PowerMode::OFF); - - auto modulatorPtr = sp<scheduler::VsyncModulator>::make( - mFlinger->mVsyncConfiguration->getCurrentConfigs()); - - mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), - std::move(vsyncTracker), mRefreshRateSelector, - std::move(modulatorPtr), *(callback ?: this), - *(vsyncTrackerCallback ?: this)); - - mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); - mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); - resetScheduler(mScheduler); - } - - void resetScheduler(scheduler::Scheduler *scheduler) { mFlinger->mScheduler.reset(scheduler); } - - scheduler::TestableScheduler &mutableScheduler() const { return *mScheduler; } - - using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction; - void setCreateBufferQueueFunction(CreateBufferQueueFunction f) { - mFactory.mCreateBufferQueue = f; - } - - using CreateNativeWindowSurfaceFunction = - surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction; - void setCreateNativeWindowSurface(CreateNativeWindowSurfaceFunction f) { - mFactory.mCreateNativeWindowSurface = f; - } - - void setInternalDisplayPrimaries(const ui::DisplayPrimaries &primaries) { - memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries)); - } - - static auto &mutableLayerDrawingState(const sp<Layer> &layer) { return layer->mDrawingState; } - - auto &mutableStateLock() { return mFlinger->mStateLock; } - - static auto findOutputLayerForDisplay(const sp<Layer> &layer, - const sp<const DisplayDevice> &display) { - return layer->findOutputLayerForDisplay(display.get()); - } - - /* ------------------------------------------------------------------------ - * Forwarding for functions being tested - */ - - void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); } - - void commitTransactionsLocked(uint32_t transactionFlags) FTL_FAKE_GUARD(kMainThreadContext) { - Mutex::Autolock lock(mFlinger->mStateLock); - mFlinger->commitTransactionsLocked(transactionFlags); - } - - auto setDisplayStateLocked(const DisplayState &s) { - Mutex::Autolock lock(mFlinger->mStateLock); - return mFlinger->setDisplayStateLocked(s); - } - - auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); } - - // Allow reading display state without locking, as if called on the SF main thread. - auto setPowerModeInternal(const sp<DisplayDevice> &display, - hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS { - return mFlinger->setPowerModeInternal(display, mode); - } - - auto &getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; } - auto &getPendingTransactionQueue() { - return mFlinger->mTransactionHandler.mPendingTransactionQueues; - } - - auto setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, - const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, - bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers, - bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks, - uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) { - return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken, - inputWindowCommands, desiredPresentTime, - isAutoTimestamp, uncacheBuffers, hasListenerCallbacks, - listenerCallbacks, transactionId, - mergedTransactionIds); - } - - auto flushTransactionQueues() { - ftl::FakeGuard guard(kMainThreadContext); - return mFlinger->flushTransactionQueues(VsyncId{0}); - } - - auto onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { - return mFlinger->onTransact(code, data, reply, flags); - } - - auto getGpuContextPriority() { return mFlinger->getGpuContextPriority(); } - - auto calculateMaxAcquiredBufferCount(Fps refreshRate, - std::chrono::nanoseconds presentLatency) const { - return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency); - } - - /* Read-write access to private data to set up preconditions and assert - * post-conditions. - */ - auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; } - auto& mutableCurrentState() { return mFlinger->mCurrentState; } - auto& mutableDisplays() { return mFlinger->mDisplays; } - auto& mutableDrawingState() { return mFlinger->mDrawingState; } - - auto fromHandle(const sp<IBinder> &handle) { return LayerHandle::getLayer(handle); } - - ~TestableSurfaceFlinger() { - mutableDisplays().clear(); - mutableCurrentState().displays.clear(); - mutableDrawingState().displays.clear(); - mFlinger->mScheduler.reset(); - mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); - mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>(); - mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); - } - -private: - void requestHardwareVsync(PhysicalDisplayId, bool) override {} - void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {} - void kernelTimerChanged(bool) override {} - void triggerOnFrameRateOverridesChanged() override {} - void onChoreographerAttached() override {} - - // IVsyncTrackerCallback overrides - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} - - surfaceflinger::test::Factory mFactory; - sp<SurfaceFlinger> mFlinger = - sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization); - scheduler::TestableScheduler *mScheduler = nullptr; - std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector; -}; - -} // namespace android diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp deleted file mode 100644 index 7aae3c4a1a..0000000000 --- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2021 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 <Client.h> -#include <DisplayDevice.h> -#include <LayerRenderArea.h> -#include <ftl/future.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <gui/IProducerListener.h> -#include <gui/LayerDebugInfo.h> -#include <gui/SurfaceComposerClient.h> -#include <gui/WindowInfo.h> -#include <renderengine/mock/FakeExternalTexture.h> -#include <ui/DisplayStatInfo.h> -#include <ui/Transform.h> - -#include <FuzzableDataspaces.h> -#include <surfaceflinger_fuzzers_utils.h> - -namespace android::fuzzer { -using namespace renderengine; - -constexpr uint16_t kRandomStringLength = 256; - -class LayerFuzzer { -public: - LayerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void init(); - void invokeBufferStateLayer(); - void invokeEffectLayer(); - LayerCreationArgs createLayerCreationArgs(TestableSurfaceFlinger* flinger, sp<Client> client); - Rect getFuzzedRect(); - ui::Transform getFuzzedTransform(); - FrameTimelineInfo getFuzzedFrameTimelineInfo(); - -private: - FuzzedDataProvider mFdp; -}; - -Rect LayerFuzzer::getFuzzedRect() { - return Rect(mFdp.ConsumeIntegral<int32_t>() /*left*/, mFdp.ConsumeIntegral<int32_t>() /*top*/, - mFdp.ConsumeIntegral<int32_t>() /*right*/, - mFdp.ConsumeIntegral<int32_t>() /*bottom*/); -} - -ui::Transform LayerFuzzer::getFuzzedTransform() { - return ui::Transform(mFdp.ConsumeIntegral<int32_t>() /*orientation*/, - mFdp.ConsumeIntegral<int32_t>() /*width*/, - mFdp.ConsumeIntegral<int32_t>() /*height*/); -} - -FrameTimelineInfo LayerFuzzer::getFuzzedFrameTimelineInfo() { - FrameTimelineInfo ftInfo; - ftInfo.vsyncId = mFdp.ConsumeIntegral<int64_t>(); - ftInfo.inputEventId = mFdp.ConsumeIntegral<int32_t>(); - return ftInfo; -} - -LayerCreationArgs LayerFuzzer::createLayerCreationArgs(TestableSurfaceFlinger* flinger, - sp<Client> client) { - flinger->setupScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_unique<android::mock::VSyncTracker>(), - std::make_unique<android::mock::EventThread>(), - std::make_unique<android::mock::EventThread>()); - - return LayerCreationArgs(flinger->flinger(), client, - mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/, - mFdp.ConsumeIntegral<uint32_t>() /*flags*/, {} /*metadata*/); -} - -void LayerFuzzer::invokeEffectLayer() { - TestableSurfaceFlinger flinger; - sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger())); - const LayerCreationArgs layerCreationArgs = createLayerCreationArgs(&flinger, client); - sp<Layer> effectLayer = sp<Layer>::make(layerCreationArgs); - - effectLayer->setColor({(mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*x*/, - mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*y*/, - mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*z*/)}); - effectLayer->setDataspace(mFdp.PickValueInArray(kDataspaces)); - sp<Layer> parent = sp<Layer>::make(layerCreationArgs); - effectLayer->setChildrenDrawingParent(parent); - - const FrameTimelineInfo frameInfo = getFuzzedFrameTimelineInfo(); - const int64_t postTime = mFdp.ConsumeIntegral<int64_t>(); - effectLayer->setFrameTimelineVsyncForBufferTransaction(frameInfo, postTime); - effectLayer->setFrameTimelineVsyncForBufferlessTransaction(frameInfo, postTime); - auto surfaceFrame = effectLayer->createSurfaceFrameForTransaction(frameInfo, postTime); - auto surfaceFrame1 = - effectLayer->createSurfaceFrameForBuffer(frameInfo, postTime, - mFdp.ConsumeRandomLengthString( - kRandomStringLength) /*bufferName*/); - effectLayer->addSurfaceFramePresentedForBuffer(surfaceFrame, - mFdp.ConsumeIntegral<int64_t>() /*acquireTime*/, - mFdp.ConsumeIntegral<int64_t>() /*currentTime*/); - effectLayer->addSurfaceFrameDroppedForBuffer(surfaceFrame1, mFdp.ConsumeIntegral<nsecs_t>()); - - parent.clear(); - client.clear(); - effectLayer.clear(); -} - -void LayerFuzzer::invokeBufferStateLayer() { - TestableSurfaceFlinger flinger; - sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger())); - sp<Layer> layer = sp<Layer>::make(createLayerCreationArgs(&flinger, client)); - sp<Fence> fence = sp<Fence>::make(); - const std::shared_ptr<FenceTime> fenceTime = std::make_shared<FenceTime>(fence); - - const CompositorTiming compositorTiming(mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>()); - - layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(), - ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>())); - layer->onLayerDisplayed(ftl::yield<FenceResult>( - base::unexpected(mFdp.ConsumeIntegral<status_t>())) - .share(), - ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>())); - - layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>()); - layer->onCompositionPresented(nullptr, fenceTime, fenceTime, compositorTiming); - - layer->setTransform(mFdp.ConsumeIntegral<uint32_t>()); - layer->setTransformToDisplayInverse(mFdp.ConsumeBool()); - layer->setCrop(getFuzzedRect()); - - layer->setHdrMetadata(getFuzzedHdrMetadata(&mFdp)); - layer->setDataspace(mFdp.PickValueInArray(kDataspaces)); - if (mFdp.ConsumeBool()) { - layer->setSurfaceDamageRegion(Region()); - layer->setTransparentRegionHint(Region()); - } else { - layer->setSurfaceDamageRegion(Region(getFuzzedRect())); - layer->setTransparentRegionHint(Region(getFuzzedRect())); - } - layer->setApi(mFdp.ConsumeIntegral<int32_t>()); - - native_handle_t* testHandle = native_handle_create(0, 1); - const bool ownsHandle = mFdp.ConsumeBool(); - sp<NativeHandle> nativeHandle = sp<NativeHandle>::make(testHandle, ownsHandle); - layer->setSidebandStream(nativeHandle, getFuzzedFrameTimelineInfo(), - mFdp.ConsumeIntegral<nsecs_t>() /* postTime */); - layer->computeSourceBounds(getFuzzedFloatRect(&mFdp)); - - layer->fenceHasSignaled(); - layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>()); - const std::vector<sp<CallbackHandle>> callbacks; - layer->setTransactionCompletedListeners(callbacks, mFdp.ConsumeBool()); - - std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< - renderengine::mock::FakeExternalTexture>(mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint64_t>(), - static_cast<android::PixelFormat>( - mFdp.PickValueInArray(kPixelFormats)), - mFdp.ConsumeIntegral<uint64_t>()); - layer->setBuffer(texture, {} /*bufferData*/, mFdp.ConsumeIntegral<nsecs_t>() /*postTime*/, - mFdp.ConsumeIntegral<nsecs_t>() /*desiredTime*/, - mFdp.ConsumeBool() /*isAutoTimestamp*/, - {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/); - - LayerRenderArea layerArea(*(flinger.flinger()), layer, getFuzzedRect(), - {mFdp.ConsumeIntegral<int32_t>(), - mFdp.ConsumeIntegral<int32_t>()} /*reqSize*/, - mFdp.PickValueInArray(kDataspaces), mFdp.ConsumeBool(), - mFdp.ConsumeBool(), getFuzzedTransform(), getFuzzedRect(), - mFdp.ConsumeBool()); - layerArea.render([]() {} /*drawLayers*/); - - if (!ownsHandle) { - native_handle_close(testHandle); - native_handle_delete(testHandle); - } - nativeHandle.clear(); - fence.clear(); - client.clear(); - layer.clear(); -} - -void LayerFuzzer::init() { - invokeBufferStateLayer(); - invokeEffectLayer(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - LayerFuzzer layerFuzzer(data, size); - layerFuzzer.init(); - return 0; -} - -} // namespace android::fuzzer diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp deleted file mode 100644 index b690d8d98e..0000000000 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright 2021 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 <ftl/enum.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <processgroup/sched_policy.h> - -#include <scheduler/IVsyncSource.h> -#include <scheduler/PresentLatencyTracker.h> - -#include "Scheduler/OneShotTimer.h" -#include "Scheduler/RefreshRateSelector.h" -#include "Scheduler/VSyncDispatchTimerQueue.h" -#include "Scheduler/VSyncPredictor.h" -#include "Scheduler/VSyncReactor.h" - -#include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/MockVSyncDispatch.h" -#include "mock/MockVSyncTracker.h" - -#include "surfaceflinger_fuzzers_utils.h" -#include "surfaceflinger_scheduler_fuzzer.h" - -namespace android::fuzz { - -using hardware::graphics::composer::hal::PowerMode; - -constexpr nsecs_t kVsyncPeriods[] = {(30_Hz).getPeriodNsecs(), (60_Hz).getPeriodNsecs(), - (72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(), - (120_Hz).getPeriodNsecs()}; - -constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>(); -constexpr auto kCompositionCoverage = ftl::enum_range<CompositionCoverage>(); - -constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF, - PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND}; - -constexpr uint16_t kRandomStringLength = 256; -constexpr std::chrono::duration kSyncPeriod(16ms); -constexpr PhysicalDisplayId kDisplayId = PhysicalDisplayId::fromPort(42u); - -template <typename T> -void dump(T* component, FuzzedDataProvider* fdp) { - std::string res = fdp->ConsumeRandomLengthString(kRandomStringLength); - component->dump(res); -} - -inline sp<Fence> makeFakeFence() { - return sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING)); -} - -class SchedulerFuzzer { -public: - SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - void fuzzRefreshRateSelection(); - void fuzzRefreshRateSelector(); - void fuzzPresentLatencyTracker(); - void fuzzFrameTargeter(); - void fuzzVSyncModulator(); - void fuzzVSyncPredictor(); - void fuzzVSyncReactor(); - void fuzzLayerHistory(); - void fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch); - void fuzzVSyncDispatchTimerQueue(); - void fuzzOneShotTimer(); - void fuzzEventThread(); - PhysicalDisplayId getPhysicalDisplayId(); - - FuzzedDataProvider mFdp; - - std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule; -}; - -PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() { - PhysicalDisplayId internalDispId = PhysicalDisplayId::fromPort(111u); - PhysicalDisplayId externalDispId = PhysicalDisplayId::fromPort(222u); - PhysicalDisplayId randomDispId = PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint16_t>()); - PhysicalDisplayId dispId64Bit = PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu); - PhysicalDisplayId displayId = mFdp.PickValueInArray<PhysicalDisplayId>( - {internalDispId, externalDispId, dispId64Bit, randomDispId}); - return displayId; -} - -struct EventThreadCallback : public IEventThreadCallback { - bool throttleVsync(TimePoint, uid_t) override { return false; } - Period getVsyncPeriod(uid_t) override { return kSyncPeriod; } - void resync() override {} -}; - -void SchedulerFuzzer::fuzzEventThread() { - mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>( - new scheduler::VsyncSchedule(getPhysicalDisplayId(), - std::make_shared<mock::VSyncTracker>(), - std::make_shared<mock::VSyncDispatch>(), nullptr)); - EventThreadCallback callback; - std::unique_ptr<android::impl::EventThread> thread = std::make_unique< - android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, callback, - (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), - (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); - - thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool()); - sp<EventThreadConnection> connection = - sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>()); - thread->requestNextVsync(connection); - thread->setVsyncRate(mFdp.ConsumeIntegral<uint32_t>() /*rate*/, connection); - - thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), - (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); - thread->registerDisplayEventConnection(connection); - thread->enableSyntheticVsync(mFdp.ConsumeBool()); - dump<android::impl::EventThread>(thread.get(), &mFdp); -} - -void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch) { - scheduler::VSyncDispatch::CallbackToken tmp = dispatch->registerCallback( - [&](auto, auto, auto) { - dispatch->schedule(tmp, - {.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); - }, - "o.o"); - dispatch->schedule(tmp, - {.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); - dispatch->unregisterCallback(tmp); - dispatch->cancel(tmp); -} - -void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { - auto stubTracker = std::make_shared<FuzzImplVSyncTracker>(mFdp.ConsumeIntegral<nsecs_t>()); - scheduler::VSyncDispatchTimerQueue - mDispatch{std::make_unique<scheduler::ControllableClock>(), stubTracker, - mFdp.ConsumeIntegral<nsecs_t>() /*dispatchGroupThreshold*/, - mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/}; - - fuzzCallbackToken(&mDispatch); - - dump<scheduler::VSyncDispatchTimerQueue>(&mDispatch, &mFdp); - - scheduler::VSyncDispatchTimerQueueEntry entry( - "fuzz", [](auto, auto, auto) {}, - mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/); - entry.update(*stubTracker, 0); - entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}, - *stubTracker, 0); - entry.disarm(); - entry.ensureNotRunning(); - entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}, - *stubTracker, 0); - auto const wakeup = entry.wakeupTime(); - auto const ready = entry.readyTime(); - entry.callback(entry.executing(), *wakeup, *ready); - entry.addPendingWorkloadUpdate({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), - .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); - dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp); -} - -struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback { - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} -}; - -void SchedulerFuzzer::fuzzVSyncPredictor() { - uint16_t now = mFdp.ConsumeIntegral<uint16_t>(); - uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); - uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); - nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX); - VsyncTrackerCallback callback; - const auto mode = ftl::as_non_null( - mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(idealPeriod))); - scheduler::VSyncPredictor tracker{mode, historySize, minimumSamplesForPrediction, - mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/, - callback}; - uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); - tracker.setDisplayModePtr(ftl::as_non_null( - mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(period)))); - for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) { - if (!tracker.needsMoreSamples()) { - break; - } - tracker.addVsyncTimestamp(now += period); - } - tracker.nextAnticipatedVSyncTimeFrom(now); - tracker.resetModel(); -} - -void SchedulerFuzzer::fuzzOneShotTimer() { - FakeClock* clock = new FakeClock(); - std::unique_ptr<scheduler::OneShotTimer> idleTimer = std::make_unique<scheduler::OneShotTimer>( - mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/, - (std::chrono::milliseconds)mFdp.ConsumeIntegral<uint8_t>() /*val*/, - [] {} /*resetCallback*/, [] {} /*timeoutCallback*/, std::unique_ptr<FakeClock>(clock)); - idleTimer->start(); - idleTimer->reset(); - idleTimer->stop(); -} - -void SchedulerFuzzer::fuzzLayerHistory() { - TestableSurfaceFlinger flinger; - flinger.setupScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_unique<android::mock::VSyncTracker>(), - std::make_unique<android::mock::EventThread>(), - std::make_unique<android::mock::EventThread>()); - flinger.setupTimeStats(std::make_unique<android::mock::TimeStats>()); - std::unique_ptr<android::renderengine::RenderEngine> renderEngine = - std::make_unique<android::renderengine::mock::RenderEngine>(); - flinger.setupRenderEngine(std::move(renderEngine)); - flinger.setupComposer(std::make_unique<android::Hwc2::mock::Composer>()); - - scheduler::TestableScheduler* scheduler = flinger.scheduler(); - - scheduler::LayerHistory& historyV1 = scheduler->mutableLayerHistory(); - nsecs_t time1 = systemTime(); - nsecs_t time2 = time1; - uint8_t historySize = mFdp.ConsumeIntegral<uint8_t>(); - - sp<FuzzImplLayer> layer1 = sp<FuzzImplLayer>::make(flinger.flinger()); - sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger()); - - for (int i = 0; i < historySize; ++i) { - historyV1.record(layer1->getSequence(), layer1->getLayerProps(), time1, time1, - scheduler::LayerHistory::LayerUpdateType::Buffer); - historyV1.record(layer2->getSequence(), layer2->getLayerProps(), time2, time2, - scheduler::LayerHistory::LayerUpdateType::Buffer); - time1 += mFdp.PickValueInArray(kVsyncPeriods); - time2 += mFdp.PickValueInArray(kVsyncPeriods); - } - historyV1.summarize(*scheduler->refreshRateSelector(), time1); - historyV1.summarize(*scheduler->refreshRateSelector(), time2); - - scheduler->createConnection(std::make_unique<android::mock::EventThread>()); - - scheduler::ConnectionHandle handle; - scheduler->createDisplayEventConnection(handle); - scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), - (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); - - std::string result = mFdp.ConsumeRandomLengthString(kRandomStringLength); - utils::Dumper dumper(result); - scheduler->dump(dumper); -} - -void SchedulerFuzzer::fuzzVSyncReactor() { - std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>(); - scheduler::VSyncReactor reactor(kDisplayId, - std::make_unique<ClockWrapper>( - std::make_shared<FuzzImplClock>()), - *vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/, - false); - - const auto mode = ftl::as_non_null( - mock::createDisplayMode(DisplayModeId(0), - Fps::fromPeriodNsecs(mFdp.ConsumeIntegral<nsecs_t>()))); - reactor.onDisplayModeChanged(mode, mFdp.ConsumeBool()); - bool periodFlushed = false; // Value does not matter, since this is an out - // param from addHwVsyncTimestamp. - reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed); - reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt, - &periodFlushed); - - const auto fence = std::make_shared<FenceTime>(makeFakeFence()); - vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>()); - FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>()); - fence->applyTrustedSnapshot(snap); - reactor.setIgnorePresentFences(mFdp.ConsumeBool()); - reactor.addPresentFence(fence); - dump<scheduler::VSyncReactor>(&reactor, &mFdp); -} - -void SchedulerFuzzer::fuzzVSyncModulator() { - enum { - SF_OFFSET_LATE, - APP_OFFSET_LATE, - SF_DURATION_LATE, - APP_DURATION_LATE, - SF_OFFSET_EARLY, - APP_OFFSET_EARLY, - SF_DURATION_EARLY, - APP_DURATION_EARLY, - SF_OFFSET_EARLY_GPU, - APP_OFFSET_EARLY_GPU, - SF_DURATION_EARLY_GPU, - APP_DURATION_EARLY_GPU, - HWC_MIN_WORK_DURATION, - }; - using Schedule = scheduler::TransactionSchedule; - using nanos = std::chrono::nanoseconds; - using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator; - const scheduler::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE), - nanos(APP_DURATION_LATE)}; - const scheduler::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU, - nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)}; - const scheduler::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU), - nanos(APP_DURATION_EARLY_GPU)}; - const scheduler::VsyncConfigSet offsets = {early, earlyGpu, late, nanos(HWC_MIN_WORK_DURATION)}; - sp<FuzzImplVsyncModulator> vSyncModulator = - sp<FuzzImplVsyncModulator>::make(offsets, scheduler::Now); - (void)vSyncModulator->setVsyncConfigSet(offsets); - (void)vSyncModulator->setTransactionSchedule(Schedule::Late); - const auto token = sp<BBinder>::make(); - (void)vSyncModulator->setTransactionSchedule(Schedule::EarlyStart, token); - vSyncModulator->binderDied(token); -} - -void SchedulerFuzzer::fuzzRefreshRateSelection() { - TestableSurfaceFlinger flinger; - flinger.setupScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_unique<android::mock::VSyncTracker>(), - std::make_unique<android::mock::EventThread>(), - std::make_unique<android::mock::EventThread>()); - - sp<Client> client; - LayerCreationArgs args(flinger.flinger(), client, - mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/, - mFdp.ConsumeIntegral<uint16_t>() /*layerFlags*/, LayerMetadata()); - sp<Layer> layer = sp<Layer>::make(args); - - layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>()); -} - -void SchedulerFuzzer::fuzzRefreshRateSelector() { - using RefreshRateSelector = scheduler::RefreshRateSelector; - using LayerRequirement = RefreshRateSelector::LayerRequirement; - using RefreshRateStats = scheduler::RefreshRateStats; - - const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1); - const uint16_t maxRefreshRate = - mFdp.ConsumeIntegralInRange<uint16_t>(minRefreshRate + 1, UINT16_MAX); - - const DisplayModeId modeId{mFdp.ConsumeIntegralInRange<uint8_t>(0, 10)}; - - DisplayModes displayModes; - for (uint16_t fps = minRefreshRate; fps < maxRefreshRate; ++fps) { - displayModes.try_emplace(modeId, - mock::createDisplayMode(modeId, - Fps::fromValue(static_cast<float>(fps)))); - } - - RefreshRateSelector refreshRateSelector(displayModes, modeId); - - const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false}; - std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}}; - - refreshRateSelector.getRankedFrameRates(layers, globalSignals); - - layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength); - layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>(); - layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()); - layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values); - auto frameRateOverrides = - refreshRateSelector.getFrameRateOverrides(layers, - Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>()), - globalSignals); - - { - ftl::FakeGuard guard(kMainThreadContext); - - refreshRateSelector.setPolicy( - RefreshRateSelector:: - DisplayManagerPolicy{modeId, - {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}}); - refreshRateSelector.setPolicy( - RefreshRateSelector::OverridePolicy{modeId, - {Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>())}}); - refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{}); - - refreshRateSelector.setActiveMode(modeId, - Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); - } - - RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>())); - RefreshRateSelector::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); - - android::mock::TimeStats timeStats; - RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), - PowerMode::OFF); - - const auto fpsOpt = displayModes.get(modeId).transform( - [](const DisplayModePtr& mode) { return mode->getVsyncRate(); }); - refreshRateStats.setRefreshRate(*fpsOpt); - - refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes)); -} - -void SchedulerFuzzer::fuzzPresentLatencyTracker() { - scheduler::PresentLatencyTracker tracker; - - int i = 5; - while (i-- > 0) { - tracker.trackPendingFrame(getFuzzedTimePoint(mFdp), - std::make_shared<FenceTime>(makeFakeFence())); - } -} - -void SchedulerFuzzer::fuzzFrameTargeter() { - scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool()); - - const struct VsyncSource final : scheduler::IVsyncSource { - explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {} - FuzzedDataProvider& fuzzer; - - Period period() const { return getFuzzedDuration(fuzzer); } - TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); } - Period minFramePeriod() const { return period(); } - } vsyncSource{mFdp}; - - int i = 10; - while (i-- > 0) { - frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp), - .vsyncId = getFuzzedVsyncId(mFdp), - .expectedVsyncTime = getFuzzedTimePoint(mFdp), - .sfWorkDuration = getFuzzedDuration(mFdp)}, - vsyncSource); - - frameTargeter.setPresentFence(makeFakeFence()); - - frameTargeter.endFrame( - {.compositionCoverage = mFdp.PickValueInArray(kCompositionCoverage.values)}); - } -} - -void SchedulerFuzzer::process() { - fuzzRefreshRateSelection(); - fuzzRefreshRateSelector(); - fuzzPresentLatencyTracker(); - fuzzFrameTargeter(); - fuzzVSyncModulator(); - fuzzVSyncPredictor(); - fuzzVSyncReactor(); - fuzzLayerHistory(); - fuzzEventThread(); - fuzzVSyncDispatchTimerQueue(); - fuzzOneShotTimer(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - SchedulerFuzzer schedulerFuzzer(data, size); - schedulerFuzzer.process(); - return 0; -} - -} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h deleted file mode 100644 index fa307e9bb4..0000000000 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2021 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. - * - */ - -/* - Reference for some of the classes and functions has been taken from unittests - present in frameworks/native/services/surfaceflinger/tests/unittests -*/ - -#pragma once - -#include <scheduler/TimeKeeper.h> - -#include "Clock.h" -#include "Layer.h" -#include "Scheduler/EventThread.h" -#include "Scheduler/Scheduler.h" -#include "Scheduler/VSyncTracker.h" -#include "Scheduler/VsyncModulator.h" - -namespace android::fuzz { - -class FuzzImplClock : public android::scheduler::Clock { -public: - nsecs_t now() const { return 1; } -}; - -class ClockWrapper : public android::scheduler::Clock { -public: - ClockWrapper(std::shared_ptr<android::scheduler::Clock> const& clock) : mClock(clock) {} - - nsecs_t now() const { return mClock->now(); } - -private: - std::shared_ptr<android::scheduler::Clock> const mClock; -}; - -} // namespace android::fuzz - -namespace android { - -using namespace std::chrono_literals; - -class FakeClock : public Clock { -public: - virtual ~FakeClock() = default; - std::chrono::steady_clock::time_point now() const override { return mNow; } - - void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; } - -private: - std::chrono::steady_clock::time_point mNow; -}; - -class FuzzImplLayer : public Layer { -public: - FuzzImplLayer(SurfaceFlinger* flinger, std::string name) - : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {} - explicit FuzzImplLayer(SurfaceFlinger* flinger) : FuzzImplLayer(flinger, "FuzzLayer") {} - - const char* getType() const override { return ""; } - - bool isVisible() const override { return true; } - - sp<Layer> createClone(uint32_t /* mirrorRootId */) override { return nullptr; } -}; - -class FuzzImplVSyncTracker : public scheduler::VSyncTracker { -public: - FuzzImplVSyncTracker(nsecs_t period) { mPeriod = period; } - - FuzzImplVSyncTracker() = default; - - bool addVsyncTimestamp(nsecs_t /* timestamp */) override { return true; } - - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */) const override { return 1; } - - nsecs_t currentPeriod() const override { return 1; } - Period minFramePeriod() const override { return Period::fromNs(currentPeriod()); } - - void resetModel() override {} - - bool needsMoreSamples() const override { return true; } - - bool isVSyncInPhase(nsecs_t /* timePoint */, Fps /* frameRate */) const override { - return true; - } - - void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) override {} - - nsecs_t nextVSyncTime(nsecs_t timePoint) const { - if (timePoint % mPeriod == 0) { - return timePoint; - } - return (timePoint - (timePoint % mPeriod) + mPeriod); - } - - void setRenderRate(Fps) override {} - - void onFrameBegin(TimePoint, TimePoint) override {} - - void onFrameMissed(TimePoint) override {} - - void dump(std::string& /* result */) const override {} - -protected: - nsecs_t mPeriod; -}; - -class FuzzImplVSyncDispatch : public scheduler::VSyncDispatch { -public: - CallbackToken registerCallback(Callback /* callbackFn */, - std::string /* callbackName */) override { - return CallbackToken{}; - } - - void unregisterCallback(CallbackToken /* token */) override {} - - scheduler::ScheduleResult schedule(CallbackToken /* token */, - ScheduleTiming /* scheduleTiming */) override { - return (scheduler::ScheduleResult)0; - } - - scheduler::ScheduleResult update(CallbackToken /* token */, - ScheduleTiming /* scheduleTiming */) override { - return (scheduler::ScheduleResult)0; - } - - scheduler::CancelResult cancel(CallbackToken /* token */) override { - return (scheduler::CancelResult)0; - } - - void dump(std::string& /* result */) const override {} -}; - -} // namespace android - -namespace android::scheduler { - -class ControllableClock : public TimeKeeper { -public: - nsecs_t now() const { return 1; }; - void alarmAt(std::function<void()> /* callback */, nsecs_t /* time */) override {} - void alarmCancel() override {} - void dump(std::string& /* result */) const override {} - - void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) { - mCallback = callback; - mNextCallbackTime = time; - } - - nsecs_t fakeTime() const { return mCurrentTime; } - - void advanceToNextCallback() { - mCurrentTime = mNextCallbackTime; - if (mCallback) { - mCallback(); - } - } - - void advanceBy(nsecs_t advancement) { - mCurrentTime += advancement; - if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) { - mCallback(); - } - }; - - void setLag(nsecs_t lag) { mLag = lag; } - -private: - std::function<void()> mCallback; - nsecs_t mNextCallbackTime = 0; - nsecs_t mCurrentTime = 0; - nsecs_t mLag = 0; -}; - -static VsyncModulator::TimePoint Now() { - static VsyncModulator::TimePoint now; - return now += VsyncModulator::MIN_EARLY_TRANSACTION_TIME; -} - -class FuzzImplVsyncModulator : public VsyncModulator { -public: - FuzzImplVsyncModulator(const VsyncConfigSet& config, Now now) : VsyncModulator(config, now) {} - - void binderDied(const wp<IBinder>& token) { VsyncModulator::binderDied(token); } -}; -} // namespace android::scheduler diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp index a4dc8a058e..f77b137e4d 100644 --- a/services/surfaceflinger/layerproto/Android.bp +++ b/services/surfaceflinger/layerproto/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library { diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index abe7ec708f..5174fa7fb1 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -1,3 +1,5 @@ +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig + package: "com.android.graphics.surfaceflinger.flags" container: "system" @@ -17,13 +19,6 @@ flag { is_fixed_read_only: true } -flag{ - name: "late_boot_misc2" - namespace: "core_graphics" - description: "This flag controls minor miscellaneous SurfaceFlinger changes. Cannot be read before boot finished!" - bug: "297389311" -} - flag { name: "vrr_config" namespace: "core_graphics" @@ -32,6 +27,8 @@ flag { is_fixed_read_only: true } +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig + flag { name: "enable_layer_command_batching" namespace: "core_graphics" @@ -41,13 +38,6 @@ flag { } flag { - name: "dont_skip_on_early" - namespace: "core_graphics" - description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early" - bug: "273702768" -} - -flag { name: "multithreaded_present" namespace: "core_graphics" description: "Controls whether to offload present calls to another thread" @@ -63,6 +53,8 @@ flag { is_fixed_read_only: true } +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig + flag { name: "hotplug2" namespace: "core_graphics" @@ -87,6 +79,8 @@ flag { is_fixed_read_only: true } +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig + flag { name: "refresh_rate_overlay_on_external_display" namespace: "core_graphics" @@ -99,10 +93,23 @@ flag { namespace: "core_graphics" description: "Whether to use the closest known refresh rate to determine the fps consistency." bug: "299201319" + is_fixed_read_only: true } +# This flag is broken. +# See alternative one: cache_when_source_crop_layer_only_moved +# flag { +# name: "cache_if_source_crop_layer_only_moved" +# namespace: "core_graphics" +# description: "do not flatten layers if source crop is only moved" +# bug: "305718400" +# is_fixed_read_only: true +# } + +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig + flag { - name: "cache_if_source_crop_layer_only_moved" + name: "cache_when_source_crop_layer_only_moved" namespace: "core_graphics" description: "do not flatten layers if source crop is only moved" bug: "305718400" @@ -133,6 +140,8 @@ flag { is_fixed_read_only: true } +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig + flag { name: "game_default_frame_rate" namespace: "game" @@ -140,3 +149,77 @@ flag { bug: "286084594" is_fixed_read_only: true } + +flag { + name: "vulkan_renderengine" + namespace: "core_graphics" + description: "Use Vulkan backend in RenderEngine prior to switching to Graphite." + bug: "293371537" + is_fixed_read_only: true +} + +flag { + name: "graphite_renderengine" + namespace: "core_graphics" + description: "Use Skia's Graphite Vulkan backend in RenderEngine." + bug: "293371537" + is_fixed_read_only: true +} + +flag { + name: "screenshot_fence_preservation" + namespace: "core_graphics" + description: "Bug fix around screenshot fences" + bug: "302703346" + is_fixed_read_only: true +} + +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig + +flag { + name: "renderable_buffer_usage" + namespace: "core_graphics" + description: "Decide whether an ExternalTexture isRenderable based on its buffer's usage." + bug: "305445199" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "restore_blur_step" + namespace: "core_graphics" + description: "Restore drawing the blur input prior to drawing blurred content." + bug: "255921628" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig + +flag { + name: "dont_skip_on_early_ro" + namespace: "core_graphics" + description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early" + bug: "273702768" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "protected_if_client" + namespace: "core_graphics" + description: "Only set the RenderSurface to protected if protected layers are in client composition." + bug: "307674749" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig new file mode 100644 index 0000000000..5451752d91 --- /dev/null +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -0,0 +1,13 @@ +# IMPORTANT - please keep alphabetize to reduce merge conflicts + +package: "com.android.graphics.surfaceflinger.flags" +container: "system" + +flag { + name: "dont_skip_on_early_ro2" + namespace: "core_graphics" + description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early" + bug: "273702768" +} # dont_skip_on_early_ro2 + +# IMPORTANT - please keep alphabetize to reduce merge conflicts diff --git a/services/surfaceflinger/sysprop/Android.bp b/services/surfaceflinger/sysprop/Android.bp index f5791195db..4ea00ccc96 100644 --- a/services/surfaceflinger/sysprop/Android.bp +++ b/services/surfaceflinger/sysprop/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } sysprop_library { diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 5449aeb6bb..dab0a3f13c 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp index 03de8d0b6d..ea141f3257 100644 --- a/services/surfaceflinger/tests/LayerTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp @@ -213,6 +213,15 @@ TEST_F(LayerTransactionTest, CommitCallbackCalledOnce) { ASSERT_EQ(callCount, 1); } +TEST_F(LayerTransactionTest, AddRemoveLayers) { + for (int i = 0; i < 100; i++) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + layer.clear(); + } +} + } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp index aeceadb5d4..bce1406e9c 100644 --- a/services/surfaceflinger/tests/tracing/Android.bp +++ b/services/surfaceflinger/tests/tracing/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp index f1bb231f26..b17b529793 100644 --- a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp +++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp @@ -17,7 +17,7 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include "DisplayTransactionTestHelpers.h" +#include "DualDisplayTransactionTest.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -25,31 +25,10 @@ namespace android { namespace { -struct ActiveDisplayRotationFlagsTest : DisplayTransactionTest { - static constexpr bool kWithMockScheduler = false; - ActiveDisplayRotationFlagsTest() : DisplayTransactionTest(kWithMockScheduler) {} - +struct ActiveDisplayRotationFlagsTest + : DualDisplayTransactionTest<hal::PowerMode::ON, hal::PowerMode::OFF> { void SetUp() override { - injectMockScheduler(kInnerDisplayId); - - // Inject inner and outer displays with uninitialized power modes. - constexpr bool kInitPowerMode = false; - { - InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); - auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this); - injector.setPowerMode(std::nullopt); - injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()); - mInnerDisplay = injector.inject(); - } - { - OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); - auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this); - injector.setPowerMode(std::nullopt); - mOuterDisplay = injector.inject(); - } - - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + DualDisplayTransactionTest::SetUp(); // The flags are a static variable, so by modifying them in the test, we // are modifying the real ones used by SurfaceFlinger. Save the original @@ -64,10 +43,6 @@ struct ActiveDisplayRotationFlagsTest : DisplayTransactionTest { void TearDown() override { mFlinger.mutableActiveDisplayRotationFlags() = mOldRotationFlags; } - static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get(); - static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get(); - - sp<DisplayDevice> mInnerDisplay, mOuterDisplay; ui::Transform::RotationFlags mOldRotationFlags; }; diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index c75f90222a..f529f7cb05 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } filegroup { @@ -95,6 +96,7 @@ cc_test { "MessageQueueTest.cpp", "PowerAdvisorTest.cpp", "SmallAreaDetectionAllowMappingsTest.cpp", + "SurfaceFlinger_ColorMatrixTest.cpp", "SurfaceFlinger_CreateDisplayTest.cpp", "SurfaceFlinger_DestroyDisplayTest.cpp", "SurfaceFlinger_DisplayModeSwitching.cpp", @@ -118,6 +120,7 @@ cc_test { "RefreshRateSelectorTest.cpp", "RefreshRateStatsTest.cpp", "RegionSamplingTest.cpp", + "TestableScheduler.cpp", "TimeStatsTest.cpp", "FrameTracerTest.cpp", "TransactionApplicationTest.cpp", @@ -127,7 +130,6 @@ cc_test { "TransactionTraceWriterTest.cpp", "TransactionTracingTest.cpp", "TunnelModeEnabledReporterTest.cpp", - "StrongTypingTest.cpp", "VSyncCallbackRegistrationTest.cpp", "VSyncDispatchTimerQueueTest.cpp", "VSyncDispatchRealtimeTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h new file mode 100644 index 0000000000..34e4ba5e78 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h @@ -0,0 +1,86 @@ +/* + * 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 <gtest/gtest.h> + +#include <compositionengine/Display.h> +#include <compositionengine/mock/DisplaySurface.h> +#include <renderengine/mock/RenderEngine.h> + +#include "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockComposer.h" +#include "mock/DisplayHardware/MockPowerAdvisor.h" +#include "mock/MockTimeStats.h" +#include "mock/system/window/MockNativeWindow.h" + +namespace android { + +// Minimal setup to use TestableSurfaceFlinger::commitAndComposite. +struct CommitAndCompositeTest : testing::Test { + void SetUp() override { + mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); + mComposer = new Hwc2::mock::Composer(); + mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); + mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); + mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); + + constexpr bool kIsPrimary = true; + FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) + .setPowerMode(hal::PowerMode::ON) + .inject(&mFlinger, mComposer); + auto compostionEngineDisplayArgs = + compositionengine::DisplayCreationArgsBuilder() + .setId(DEFAULT_DISPLAY_ID) + .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) + .setPowerAdvisor(mPowerAdvisor) + .setName("Internal display") + .build(); + auto compositionDisplay = + compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), + std::move(compostionEngineDisplayArgs)); + mDisplay = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, + ui::DisplayConnectionType::Internal, HWC_DISPLAY, + kIsPrimary) + .setDisplaySurface(mDisplaySurface) + .setNativeWindow(mNativeWindow) + .setPowerMode(hal::PowerMode::ON) + .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) + .skipRegisterDisplay() + .inject(); + } + + using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; + + static constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; + static constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); + static constexpr int DEFAULT_DISPLAY_WIDTH = 1920; + static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; + + TestableSurfaceFlinger mFlinger; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); + sp<DisplayDevice> mDisplay; + sp<compositionengine::mock::DisplaySurface> mDisplaySurface = + sp<compositionengine::mock::DisplaySurface>::make(); + sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); + mock::TimeStats* mTimeStats = new mock::TimeStats(); + Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; + Hwc2::mock::Composer* mComposer = nullptr; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index beb2147c98..7d8a30a727 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -25,6 +25,7 @@ #include <compositionengine/Display.h> #include <compositionengine/mock/DisplaySurface.h> +#include <ftl/future.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <gui/IProducerListener.h> @@ -227,14 +228,6 @@ void CompositionTest::captureScreenComposition() { LayerCase::cleanup(this); } -template <class T> -std::future<T> futureOf(T obj) { - std::promise<T> resultPromise; - std::future<T> resultFuture = resultPromise.get_future(); - resultPromise.set_value(std::move(obj)); - return resultFuture; -} - /* ------------------------------------------------------------------------ * Variants for each display configuration which can be tested */ @@ -327,13 +320,13 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); - return futureOf<FenceResult>(Fence::NO_FENCE); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); } @@ -378,14 +371,14 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace); - return futureOf<FenceResult>(Fence::NO_FENCE); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); } @@ -578,7 +571,7 @@ struct BaseLayerProperties { .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -586,7 +579,8 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so gtet the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupREBufferCompositionCommonCallExpectations " @@ -627,7 +621,7 @@ struct BaseLayerProperties { .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -635,7 +629,8 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " @@ -709,7 +704,7 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -717,7 +712,8 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupInsecureREBufferCompositionCommonCallExpectations " diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 13796650c8..fa31643df1 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -80,8 +80,7 @@ void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) { std::unique_ptr<EventThread>(mEventThread), std::unique_ptr<EventThread>(mSFEventThread), TestableSurfaceFlinger::DefaultDisplayMode{displayId}, - TestableSurfaceFlinger::SchedulerCallbackImpl::kMock, - TestableSurfaceFlinger::VsyncTrackerCallbackImpl::kMock); + TestableSurfaceFlinger::SchedulerCallbackImpl::kMock); } void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) { diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index ee12276994..f26336a655 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -347,7 +347,6 @@ struct HwcDisplayVariant { // The HWC active configuration id static constexpr hal::HWConfigId HWC_ACTIVE_CONFIG_ID = 2001; - static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON; static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) { test->mFlinger.mutablePendingHotplugEvents().emplace_back( @@ -355,7 +354,7 @@ struct HwcDisplayVariant { } // Called by tests to inject a HWC display setup - template <bool kInitPowerMode = true> + template <hal::PowerMode kPowerMode = hal::PowerMode::ON> static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) { const auto displayId = DisplayVariant::DISPLAY_ID::get(); ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId)); @@ -364,22 +363,37 @@ struct HwcDisplayVariant { .setHwcDisplayId(HWC_DISPLAY_ID) .setResolution(DisplayVariant::RESOLUTION) .setActiveConfig(HWC_ACTIVE_CONFIG_ID) - .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt) + .setPowerMode(kPowerMode) .inject(&test->mFlinger, test->mComposer); } // Called by tests to inject a HWC display setup - template <bool kInitPowerMode = true> + // + // TODO(b/241285876): The `kExpectSetPowerModeOnce` argument is set to `false` by tests that + // power on/off displays several times. Replace those catch-all expectations with `InSequence` + // and `RetiresOnSaturation`. + // + template <hal::PowerMode kPowerMode = hal::PowerMode::ON, bool kExpectSetPowerModeOnce = true> static void injectHwcDisplay(DisplayTransactionTest* test) { - if constexpr (kInitPowerMode) { + if constexpr (kExpectSetPowerModeOnce) { + if constexpr (kPowerMode == hal::PowerMode::ON) { + EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), + Return(Error::NONE))); + } + + EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, kPowerMode)) + .WillOnce(Return(Error::NONE)); + } else { EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), - Return(Error::NONE))); + .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), + Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE)) - .WillOnce(Return(Error::NONE)); + EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, _)) + .WillRepeatedly(Return(Error::NONE)); } - injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test); + + injectHwcDisplayWithNoDefaultCapabilities<kPowerMode>(test); } static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( @@ -447,9 +461,11 @@ struct HwcDisplayVariant { ? IComposerClient::DisplayConnectionType::INTERNAL : IComposerClient::DisplayConnectionType::EXTERNAL; + using ::testing::AtLeast; EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), - Return(hal::V2_4::Error::NONE))); + .Times(AtLeast(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(CONNECTION_TYPE), + Return(hal::V2_4::Error::NONE))); } EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)) @@ -481,14 +497,17 @@ constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY = constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1; -template <typename PhysicalDisplay, int width, int height> +template <typename PhysicalDisplay, int width, int height, + Secure secure = (PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal) + ? Secure::TRUE + : Secure::FALSE> struct PhysicalDisplayVariant - : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, - Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY, + : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, secure, + PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL, DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, - Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY, + Async::FALSE, secure, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, PhysicalDisplay> {}; @@ -515,6 +534,7 @@ struct SecondaryDisplay { }; struct TertiaryDisplay { + static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External; static constexpr Primary PRIMARY = Primary::FALSE; static constexpr uint8_t PORT = 253; static constexpr HWDisplayId HWC_DISPLAY_ID = 1003; diff --git a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h new file mode 100644 index 0000000000..90e716ff1f --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h @@ -0,0 +1,57 @@ +/* + * Copyright 2023 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 "DisplayTransactionTestHelpers.h" + +namespace android { + +template <hal::PowerMode kInnerDisplayPowerMode, hal::PowerMode kOuterDisplayPowerMode, + bool kExpectSetPowerModeOnce = true> +struct DualDisplayTransactionTest : DisplayTransactionTest { + static constexpr bool kWithMockScheduler = false; + DualDisplayTransactionTest() : DisplayTransactionTest(kWithMockScheduler) {} + + void SetUp() override { + injectMockScheduler(kInnerDisplayId); + + { + InnerDisplayVariant::injectHwcDisplay<kInnerDisplayPowerMode, kExpectSetPowerModeOnce>( + this); + + auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this); + injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()); + injector.setPowerMode(kInnerDisplayPowerMode); + mInnerDisplay = injector.inject(); + } + { + OuterDisplayVariant::injectHwcDisplay<kOuterDisplayPowerMode, kExpectSetPowerModeOnce>( + this); + + auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this); + injector.setPowerMode(kOuterDisplayPowerMode); + mOuterDisplay = injector.inject(); + } + } + + static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get(); + static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get(); + + sp<DisplayDevice> mInnerDisplay, mOuterDisplay; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 8891c06c75..3eabe1f362 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -55,6 +55,9 @@ constexpr PhysicalDisplayId DISPLAY_ID_64BIT = constexpr std::chrono::duration VSYNC_PERIOD(16ms); +constexpr int HDCP_V1 = 2; +constexpr int HDCP_V2 = 3; + } // namespace class EventThreadTest : public testing::Test, public IEventThreadCallback { @@ -82,6 +85,7 @@ protected: bool throttleVsync(TimePoint, uid_t) override; Period getVsyncPeriod(uid_t) override; void resync() override; + void onExpectedPresentTimePosted(TimePoint) override; void setupEventThread(); sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, @@ -104,6 +108,7 @@ protected: int32_t expectedConfigId, nsecs_t expectedVsyncPeriod); void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t); + void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime); void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride>); @@ -112,14 +117,16 @@ protected: mThread->onVsync(expectedPresentationTime, timestamp, deadlineTimestamp); } + static constexpr scheduler::ScheduleResult kScheduleResult{TimePoint::fromNs(0), + TimePoint::fromNs(0)}; AsyncCallRecorderWithCannedReturn< scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken, scheduler::VSyncDispatch::ScheduleTiming)> - mVSyncCallbackScheduleRecorder{0}; + mVSyncCallbackScheduleRecorder{kScheduleResult}; AsyncCallRecorderWithCannedReturn< scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken, scheduler::VSyncDispatch::ScheduleTiming)> - mVSyncCallbackUpdateRecorder{0}; + mVSyncCallbackUpdateRecorder{kScheduleResult}; AsyncCallRecorderWithCannedReturn< scheduler::VSyncDispatch::CallbackToken (*)(scheduler::VSyncDispatch::Callback, std::string)> @@ -128,6 +135,7 @@ protected: mVSyncCallbackUnregisterRecorder; AsyncCallRecorder<void (*)()> mResyncCallRecorder; AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mOnExpectedPresentTimePostedRecorder; ConnectionEventRecorder mConnectionEventCallRecorder{0}; ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0}; @@ -187,6 +195,10 @@ void EventThreadTest::resync() { mResyncCallRecorder.recordCall(); } +void EventThreadTest::onExpectedPresentTimePosted(TimePoint expectedPresentTime) { + mOnExpectedPresentTimePostedRecorder.recordCall(expectedPresentTime.ns()); +} + void EventThreadTest::setupEventThread() { mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule, @@ -241,6 +253,12 @@ void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid EXPECT_EQ(uid, std::get<1>(args.value())); } +void EventThreadTest::expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime) { + auto args = mOnExpectedPresentTimePostedRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedPresentTime, std::get<0>(args.value())); +} + void EventThreadTest::expectVsyncEventReceivedByConnection( const char* name, ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount) { @@ -407,6 +425,7 @@ TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); + expectOnExpectedPresentTimePosted(456); // EventThread is requesting one more callback due to VsyncRequest::SingleSuppressCallback expectVSyncCallbackScheduleReceived(true); @@ -471,7 +490,7 @@ TEST_F(EventThreadTest, getLatestVsyncEventData) { mock::VSyncTracker& mockTracker = *static_cast<mock::VSyncTracker*>(&mVsyncSchedule->getTracker()); - EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_, _)) .WillOnce(Return(preferredExpectedPresentationTime)); VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection); @@ -559,16 +578,19 @@ TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) { onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); + expectOnExpectedPresentTimePosted(456); // A second event should go to the same places. onVSyncEvent(456, 123, 0); expectThrottleVsyncReceived(123, mConnectionUid); expectVsyncEventReceivedByConnection(456, 2u); + expectOnExpectedPresentTimePosted(123); // A third event should go to the same places. onVSyncEvent(789, 777, 111); expectThrottleVsyncReceived(777, mConnectionUid); expectVsyncEventReceivedByConnection(789, 3u); + expectOnExpectedPresentTimePosted(777); } TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) { @@ -833,6 +855,19 @@ TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) { expectVSyncCallbackScheduleReceived(true); } +TEST_F(EventThreadTest, postHcpLevelsChanged) { + setupEventThread(); + + mThread->onHdcpLevelsChanged(EXTERNAL_DISPLAY_ID, HDCP_V1, HDCP_V2); + auto args = mConnectionEventCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, event.header.type); + EXPECT_EQ(EXTERNAL_DISPLAY_ID, event.header.displayId); + EXPECT_EQ(HDCP_V1, event.hdcpLevelsChange.connectedLevel); + EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp index 0c820fb291..0adf0b617a 100644 --- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp @@ -28,6 +28,7 @@ namespace android { +using namespace com::android::graphics::surfaceflinger; using testing::Return; class TestableFlagManager : public FlagManager { @@ -86,26 +87,26 @@ TEST_F(FlagManagerTest, legacyReturnsValue) { TEST_F(FlagManagerTest, creashesIfQueriedBeforeBoot) { mFlagManager.markBootIncomplete(); - EXPECT_DEATH(FlagManager::getInstance().late_boot_misc2(), ""); + EXPECT_DEATH(FlagManager::getInstance() + .refresh_rate_overlay_on_external_display(), ""); } TEST_F(FlagManagerTest, returnsOverrideTrue) { mFlagManager.markBootCompleted(); - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, false); + SET_FLAG_FOR_TEST(flags::refresh_rate_overlay_on_external_display, false); // This is stored in a static variable, so this test depends on the fact // that this flag has not been read in this process. EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(true)); - EXPECT_TRUE(mFlagManager.late_boot_misc2()); + EXPECT_TRUE(mFlagManager.refresh_rate_overlay_on_external_display()); // Further calls will not result in further calls to getBoolProperty. - EXPECT_TRUE(mFlagManager.late_boot_misc2()); + EXPECT_TRUE(mFlagManager.refresh_rate_overlay_on_external_display()); } TEST_F(FlagManagerTest, returnsOverrideReadonly) { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::add_sf_skipped_frames_to_trace, - false); + SET_FLAG_FOR_TEST(flags::add_sf_skipped_frames_to_trace, false); // This is stored in a static variable, so this test depends on the fact // that this flag has not been read in this process. @@ -113,12 +114,13 @@ TEST_F(FlagManagerTest, returnsOverrideReadonly) { EXPECT_TRUE(mFlagManager.add_sf_skipped_frames_to_trace()); } -TEST_F(FlagManagerTest, returnsOverrideFalse) { +// disabling this test since we need to use a unique flag for this test, +// but we only one server flag currently. Re-enable once we have a new flag +// and change this test to use a unique flag. +TEST_F(FlagManagerTest, DISABLED_returnsOverrideFalse) { mFlagManager.markBootCompleted(); - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags:: - refresh_rate_overlay_on_external_display, - true); + SET_FLAG_FOR_TEST(flags::refresh_rate_overlay_on_external_display, true); // This is stored in a static variable, so this test depends on the fact // that this flag has not been read in this process. @@ -129,7 +131,7 @@ TEST_F(FlagManagerTest, returnsOverrideFalse) { TEST_F(FlagManagerTest, ignoresOverrideInUnitTestMode) { mFlagManager.setUnitTestMode(); - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::multithreaded_present, true); + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); // If this has not been called in this process, it will be called. // Regardless, the result is ignored. @@ -144,13 +146,13 @@ TEST_F(FlagManagerTest, returnsValue) { EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt)); { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, true); - EXPECT_EQ(true, mFlagManager.late_boot_misc2()); + SET_FLAG_FOR_TEST(flags::refresh_rate_overlay_on_external_display, true); + EXPECT_EQ(true, mFlagManager.refresh_rate_overlay_on_external_display()); } { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, false); - EXPECT_EQ(false, mFlagManager.late_boot_misc2()); + SET_FLAG_FOR_TEST(flags::refresh_rate_overlay_on_external_display, false); + EXPECT_EQ(false, mFlagManager.refresh_rate_overlay_on_external_display()); } } @@ -160,28 +162,14 @@ TEST_F(FlagManagerTest, readonlyReturnsValue) { EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt)); { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::misc1, true); + SET_FLAG_FOR_TEST(flags::misc1, true); EXPECT_EQ(true, mFlagManager.misc1()); } { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::misc1, false); + SET_FLAG_FOR_TEST(flags::misc1, false); EXPECT_EQ(false, mFlagManager.misc1()); } } -TEST_F(FlagManagerTest, dontSkipOnEarlyIsNotCached) { - EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt)); - - const auto initialValue = com::android::graphics::surfaceflinger::flags::dont_skip_on_early(); - - com::android::graphics::surfaceflinger::flags::dont_skip_on_early(true); - EXPECT_EQ(true, mFlagManager.dont_skip_on_early()); - - com::android::graphics::surfaceflinger::flags::dont_skip_on_early(false); - EXPECT_EQ(false, mFlagManager.dont_skip_on_early()); - - com::android::graphics::surfaceflinger::flags::dont_skip_on_early(initialValue); -} - } // namespace android diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp index f695b096a7..9e8e306621 100644 --- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp @@ -24,7 +24,11 @@ #include <gtest/gtest.h> #include <gui/LayerMetadata.h> +#include "Client.h" // temporarily needed for LayerCreationArgs #include "FpsReporter.h" +#include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerHierarchy.h" +#include "FrontEnd/LayerLifecycleManager.h" #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "fake/FakeClock.h" @@ -76,7 +80,15 @@ protected: sp<Layer> createBufferStateLayer(LayerMetadata metadata); - TestableSurfaceFlinger mFlinger; + LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId, + LayerMetadata metadata); + + void createRootLayer(uint32_t id, LayerMetadata metadata); + + void createLayer(uint32_t id, uint32_t parentId, LayerMetadata metadata); + + frontend::LayerLifecycleManager mLifecycleManager; + mock::FrameTimeline mFrameTimeline = mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0); @@ -89,8 +101,8 @@ protected: sp<TestableFpsListener> mFpsListener; fake::FakeClock* mClock = new fake::FakeClock(); - sp<FpsReporter> mFpsReporter = sp<FpsReporter>::make(mFrameTimeline, *(mFlinger.flinger()), - std::unique_ptr<Clock>(mClock)); + sp<FpsReporter> mFpsReporter = + sp<FpsReporter>::make(mFrameTimeline, std::unique_ptr<Clock>(mClock)); }; FpsReporterTest::FpsReporterTest() { @@ -98,9 +110,6 @@ FpsReporterTest::FpsReporterTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mFlinger.setupMockScheduler(); - mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); - mFpsListener = sp<TestableFpsListener>::make(); } @@ -110,76 +119,94 @@ FpsReporterTest::~FpsReporterTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } -sp<Layer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) { +LayerCreationArgs FpsReporterTest::createArgs(uint32_t id, bool canBeRoot, uint32_t parentId, + LayerMetadata metadata) { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata); - return sp<Layer>::make(args); + LayerCreationArgs args(std::make_optional(id)); + args.name = "testlayer"; + args.addToRoot = canBeRoot; + args.flags = LAYER_FLAGS; + args.metadata = metadata; + args.parentId = parentId; + return args; +} + +void FpsReporterTest::createRootLayer(uint32_t id, LayerMetadata metadata = LayerMetadata()) { + std::vector<std::unique_ptr<frontend::RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<frontend::RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID, + /*metadata=*/metadata))); + mLifecycleManager.addLayers(std::move(layers)); +} + +void FpsReporterTest::createLayer(uint32_t id, uint32_t parentId, + LayerMetadata metadata = LayerMetadata()) { + std::vector<std::unique_ptr<frontend::RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<frontend::RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId, + /*mirror=*/metadata))); + mLifecycleManager.addLayers(std::move(layers)); } namespace { TEST_F(FpsReporterTest, callsListeners) { - mParent = createBufferStateLayer(); constexpr int32_t kTaskId = 12; LayerMetadata targetMetadata; targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId); - mTarget = createBufferStateLayer(targetMetadata); - mChild = createBufferStateLayer(); - mGrandChild = createBufferStateLayer(); - mUnrelated = createBufferStateLayer(); - mParent->addChild(mTarget); - mTarget->addChild(mChild); - mChild->addChild(mGrandChild); - mParent->commitChildList(); - mFlinger.mutableCurrentState().layersSortedByZ.add(mParent); - mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget); - mFlinger.mutableCurrentState().layersSortedByZ.add(mChild); - mFlinger.mutableCurrentState().layersSortedByZ.add(mGrandChild); + + createRootLayer(1, targetMetadata); + createLayer(11, 1); + createLayer(111, 11); + + frontend::LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); float expectedFps = 44.0; - EXPECT_CALL(mFrameTimeline, - computeFps(UnorderedElementsAre(mTarget->getSequence(), mChild->getSequence(), - mGrandChild->getSequence()))) + EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(1, 11, 111))) .WillOnce(Return(expectedFps)); mFpsReporter->addListener(mFpsListener, kTaskId); mClock->advanceTime(600ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps); mFpsReporter->removeListener(mFpsListener); Mock::VerifyAndClearExpectations(&mFrameTimeline); EXPECT_CALL(mFrameTimeline, computeFps(_)).Times(0); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); } TEST_F(FpsReporterTest, rateLimits) { const constexpr int32_t kTaskId = 12; LayerMetadata targetMetadata; targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId); - mTarget = createBufferStateLayer(targetMetadata); - mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget); + createRootLayer(1); + createLayer(11, 1, targetMetadata); + + frontend::LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); float firstFps = 44.0; float secondFps = 53.0; - EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence()))) + EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(11))) .WillOnce(Return(firstFps)) .WillOnce(Return(secondFps)); mFpsReporter->addListener(mFpsListener, kTaskId); mClock->advanceTime(600ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); mClock->advanceTime(200ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); mClock->advanceTime(200ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); mClock->advanceTime(200ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(secondFps, mFpsListener->lastReportedFps); } diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 6edecff6a6..2cff2f2929 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -21,6 +21,7 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <optional> #include <vector> // StrictMock<T> derives from T and is not marked final, so the destructor of T is expected to be @@ -82,6 +83,8 @@ struct HWComposerTest : testing::Test { EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE)); EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId)); } + + void setVrrTimeoutHint(bool status) { mHwc.mEnableVrrTimeout = status; } }; TEST_F(HWComposerTest, isHeadless) { @@ -99,9 +102,32 @@ TEST_F(HWComposerTest, isHeadless) { ASSERT_TRUE(mHwc.isHeadless()); } +TEST_F(HWComposerTest, getDisplayConnectionType) { + // Unknown display. + EXPECT_EQ(mHwc.getDisplayConnectionType(PhysicalDisplayId::fromPort(0)), + ui::DisplayConnectionType::Internal); + + constexpr hal::HWDisplayId kHwcDisplayId = 1; + expectHotplugConnect(kHwcDisplayId); + + const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + ASSERT_TRUE(info); + + EXPECT_CALL(*mHal, getDisplayConnectionType(kHwcDisplayId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::EXTERNAL), + Return(V2_4::Error::NONE))); + + // The first call caches the connection type. + EXPECT_EQ(mHwc.getDisplayConnectionType(info->id), ui::DisplayConnectionType::External); + + // Subsequent calls return the cached connection type. + EXPECT_EQ(mHwc.getDisplayConnectionType(info->id), ui::DisplayConnectionType::External); + EXPECT_EQ(mHwc.getDisplayConnectionType(info->id), ui::DisplayConnectionType::External); +} + TEST_F(HWComposerTest, getActiveMode) { // Unknown display. - EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), std::nullopt); + EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), ftl::Unexpected(BAD_INDEX)); constexpr hal::HWDisplayId kHwcDisplayId = 2; expectHotplugConnect(kHwcDisplayId); @@ -114,14 +140,20 @@ TEST_F(HWComposerTest, getActiveMode) { EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) .WillOnce(Return(HalError::BAD_DISPLAY)); - EXPECT_EQ(mHwc.getActiveMode(info->id), std::nullopt); + EXPECT_EQ(mHwc.getActiveMode(info->id), ftl::Unexpected(UNKNOWN_ERROR)); + } + { + EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) + .WillOnce(Return(HalError::BAD_CONFIG)); + + EXPECT_EQ(mHwc.getActiveMode(info->id), ftl::Unexpected(NO_INIT)); } { constexpr hal::HWConfigId kConfigId = 42; EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) .WillOnce(DoAll(SetArgPointee<1>(kConfigId), Return(HalError::NONE))); - EXPECT_EQ(mHwc.getActiveMode(info->id), kConfigId); + EXPECT_EQ(mHwc.getActiveMode(info->id).value_opt(), kConfigId); } } @@ -323,6 +355,7 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty()); } { + setVrrTimeoutHint(true); constexpr int32_t kWidth = 480; constexpr int32_t kHeight = 720; constexpr int32_t kConfigGroup = 1; @@ -330,10 +363,8 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { const hal::VrrConfig vrrConfig = hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(), .notifyExpectedPresentConfig = hal::VrrConfig:: - NotifyExpectedPresentConfig{.notifyExpectedPresentHeadsUpNs = - ms2ns(30), - .notifyExpectedPresentTimeoutNs = - ms2ns(30)}}; + NotifyExpectedPresentConfig{.headsUpNs = ms2ns(30), + .timeoutNs = ms2ns(30)}}; hal::DisplayConfiguration displayConfiguration{.configId = kConfigId, .width = kWidth, .height = kHeight, @@ -363,9 +394,9 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { displayConfiguration.dpi = {kDpi, kDpi}; EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _)) - .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{ - displayConfiguration}), - Return(HalError::NONE))); + .WillRepeatedly(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{ + displayConfiguration}), + Return(HalError::NONE))); modes = mHwc.getModes(info->id, kMaxFrameIntervalNs); EXPECT_EQ(modes.size(), size_t{1}); @@ -377,6 +408,10 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { EXPECT_EQ(modes.front().vrrConfig, vrrConfig); EXPECT_EQ(modes.front().dpiX, kDpi); EXPECT_EQ(modes.front().dpiY, kDpi); + + setVrrTimeoutHint(false); + modes = mHwc.getModes(info->id, kMaxFrameIntervalNs); + EXPECT_EQ(modes.front().vrrConfig->notifyExpectedPresentConfig, std::nullopt); } } @@ -437,7 +472,7 @@ TEST_F(HWComposerSetCallbackTest, loadsLayerMetadataSupport) { {kMetadata1Name, kMetadata1Mandatory}, {kMetadata2Name, kMetadata2Mandatory}, }), - Return(hardware::graphics::composer::V2_4::Error::NONE))); + Return(V2_4::Error::NONE))); EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::NONE)); EXPECT_CALL(*mHal, getHdrConversionCapabilities(_)).WillOnce(Return(HalError::NONE)); @@ -455,8 +490,7 @@ TEST_F(HWComposerSetCallbackTest, loadsLayerMetadataSupport) { TEST_F(HWComposerSetCallbackTest, handlesUnsupportedCallToGetLayerGenericMetadataKeys) { EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<aidl::Capability>{})); - EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_)) - .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED)); + EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_)).WillOnce(Return(V2_4::Error::UNSUPPORTED)); EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::UNSUPPORTED)); EXPECT_CALL(*mHal, getHdrConversionCapabilities(_)).WillOnce(Return(HalError::UNSUPPORTED)); EXPECT_CALL(*mHal, registerCallback(_)); @@ -516,7 +550,7 @@ TEST_F(HWComposerLayerGenericMetadataTest, forwardsSupportedMetadata) { setLayerGenericMetadata(kDisplayId, kLayerId, kLayerGenericMetadata1Name, kLayerGenericMetadata1Mandatory, kLayerGenericMetadata1Value)) - .WillOnce(Return(hardware::graphics::composer::V2_4::Error::NONE)); + .WillOnce(Return(V2_4::Error::NONE)); auto result = mLayer.setLayerGenericMetadata(kLayerGenericMetadata1Name, kLayerGenericMetadata1Mandatory, kLayerGenericMetadata1Value); @@ -526,7 +560,7 @@ TEST_F(HWComposerLayerGenericMetadataTest, forwardsSupportedMetadata) { setLayerGenericMetadata(kDisplayId, kLayerId, kLayerGenericMetadata2Name, kLayerGenericMetadata2Mandatory, kLayerGenericMetadata2Value)) - .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED)); + .WillOnce(Return(V2_4::Error::UNSUPPORTED)); result = mLayer.setLayerGenericMetadata(kLayerGenericMetadata2Name, kLayerGenericMetadata2Mandatory, kLayerGenericMetadata2Value); diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp index 95f19406b4..2b333f4b87 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp @@ -45,7 +45,8 @@ protected: // reparenting tests TEST_F(LayerHierarchyTest, addLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -64,7 +65,8 @@ TEST_F(LayerHierarchyTest, addLayer) { } TEST_F(LayerHierarchyTest, reparentLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(2, 11); reparentLayer(111, 12); reparentLayer(1221, 1); @@ -79,7 +81,8 @@ TEST_F(LayerHierarchyTest, reparentLayer) { } TEST_F(LayerHierarchyTest, reparentLayerToNull) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(2, UNASSIGNED_LAYER_ID); reparentLayer(11, UNASSIGNED_LAYER_ID); @@ -96,7 +99,8 @@ TEST_F(LayerHierarchyTest, reparentLayerToNull) { } TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(2, UNASSIGNED_LAYER_ID); reparentLayer(11, UNASSIGNED_LAYER_ID); reparentLayer(1221, UNASSIGNED_LAYER_ID); @@ -115,7 +119,8 @@ TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) { } TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); destroyLayerHandle(111); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -139,7 +144,8 @@ TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) { } TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, UNASSIGNED_LAYER_ID); reparentLayer(11, 1); @@ -154,7 +160,8 @@ TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) { // offscreen tests TEST_F(LayerHierarchyTest, layerMovesOnscreen) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, UNASSIGNED_LAYER_ID); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -170,7 +177,8 @@ TEST_F(LayerHierarchyTest, layerMovesOnscreen) { } TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, UNASSIGNED_LAYER_ID); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -187,7 +195,8 @@ TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) { // rel-z tests TEST_F(LayerHierarchyTest, setRelativeParent) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -200,7 +209,8 @@ TEST_F(LayerHierarchyTest, setRelativeParent) { } TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -216,7 +226,8 @@ TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) { } TEST_F(LayerHierarchyTest, reparentToRelativeParent) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -231,7 +242,8 @@ TEST_F(LayerHierarchyTest, reparentToRelativeParent) { } TEST_F(LayerHierarchyTest, setParentAsRelativeParent) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -246,7 +258,8 @@ TEST_F(LayerHierarchyTest, setParentAsRelativeParent) { } TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -262,7 +275,8 @@ TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) { } TEST_F(LayerHierarchyTest, reparentRelativeLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -294,7 +308,8 @@ TEST_F(LayerHierarchyTest, reparentRelativeLayer) { // mirror tests TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -308,7 +323,8 @@ TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) { } TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(11, UNASSIGNED_LAYER_ID); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); @@ -324,7 +340,8 @@ TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) { TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) { mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); createLayer(1111, 111); createLayer(112, 11); @@ -340,7 +357,8 @@ TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) { // mirror & relatives tests TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(111, 12); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); @@ -371,7 +389,8 @@ TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) { } TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(1221, 12); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12); @@ -401,7 +420,8 @@ TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) { } TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(11, 2); reparentLayer(2, UNASSIGNED_LAYER_ID); @@ -427,7 +447,8 @@ TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) { } TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentRelativeLayer(1221, 2); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -462,7 +483,8 @@ TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) { } TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); updateBackgroundColor(1, 0.5); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -485,7 +507,8 @@ TEST_F(LayerHierarchyTest, ParentBecomesTheChild) { createLayer(11, 1); reparentLayer(1, 11); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); std::vector<uint32_t> expectedTraversalPath = {}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -502,17 +525,11 @@ TEST_F(LayerHierarchyTest, RelativeLoops) { createLayer(11, 1); reparentRelativeLayer(11, 2); reparentRelativeLayer(2, 11); - mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); - - // fix loop - uint32_t invalidRelativeRoot; - bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot); - EXPECT_TRUE(hasRelZLoop); - mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); - hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers()); - EXPECT_EQ(invalidRelativeRoot, 11u); - EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot)); + LayerHierarchyBuilder hierarchyBuilder; + // this call is expected to fix the loop! + hierarchyBuilder.update(mLifecycleManager); + uint32_t unused; + EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(unused)); std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -534,16 +551,11 @@ TEST_F(LayerHierarchyTest, IndirectRelativeLoops) { createLayer(221, 22); reparentRelativeLayer(22, 111); reparentRelativeLayer(11, 221); - mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); - - // fix loop - uint32_t invalidRelativeRoot; - bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot); - EXPECT_TRUE(hasRelZLoop); - mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot); - hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers()); - EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot)); + LayerHierarchyBuilder hierarchyBuilder; + // this call is expected to fix the loop! + hierarchyBuilder.update(mLifecycleManager); + uint32_t unused; + EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(unused)); std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -554,7 +566,8 @@ TEST_F(LayerHierarchyTest, IndirectRelativeLoops) { } TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(1, UNASSIGNED_LAYER_ID); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -568,7 +581,8 @@ TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) { TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) { // remove default hierarchy mLifecycleManager = LayerLifecycleManager(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); createRootLayer(1); destroyLayerHandle(1); UPDATE_AND_VERIFY(hierarchyBuilder); @@ -582,7 +596,8 @@ TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) { // traversal path test TEST_F(LayerHierarchyTest, traversalPathId) { setZ(122, -1); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); auto checkTraversalPathIdVisitor = [](const LayerHierarchy& hierarchy, const LayerHierarchy::TraversalPath& traversalPath) -> bool { @@ -605,7 +620,8 @@ TEST_F(LayerHierarchyTest, zorderRespectsLayerSequenceId) { createLayer(53, 5); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); UPDATE_AND_VERIFY(hierarchyBuilder); std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 4, 5, 51, 53}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -639,7 +655,8 @@ TEST_F(LayerHierarchyTest, zorderRespectsLayerZ) { setZ(13, 1); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); UPDATE_AND_VERIFY(hierarchyBuilder); std::vector<uint32_t> expectedTraversalPath = {1, 11, 13, 12}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -661,7 +678,8 @@ TEST_F(LayerHierarchyTest, zorderRespectsLayerStack) { setLayerStack(2, 10); mLifecycleManager.commitChanges(); - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); UPDATE_AND_VERIFY(hierarchyBuilder); std::vector<uint32_t> expectedTraversalPath = {2, 21, 1, 11}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); @@ -672,7 +690,8 @@ TEST_F(LayerHierarchyTest, zorderRespectsLayerStack) { } TEST_F(LayerHierarchyTest, canMirrorDisplay) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); setLayerStack(3, 1); @@ -687,7 +706,8 @@ TEST_F(LayerHierarchyTest, canMirrorDisplay) { } TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(5)); setLayerStack(3, 1); @@ -701,7 +721,8 @@ TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) { } TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); setLayerStack(3, 1); @@ -719,7 +740,8 @@ TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) { } TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); setLayerStack(3, 1); @@ -737,7 +759,8 @@ TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) { } TEST_F(LayerHierarchyTest, canMirrorDisplayWithMirrors) { - LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); reparentLayer(12, UNASSIGNED_LAYER_ID); mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11); UPDATE_AND_VERIFY(hierarchyBuilder); diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h index 7e9abce690..67e624922c 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h @@ -176,14 +176,12 @@ protected: void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); } void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) { - if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { - hierarchyBuilder.update(mLifecycleManager.getLayers(), - mLifecycleManager.getDestroyedLayers()); - } + hierarchyBuilder.update(mLifecycleManager); mLifecycleManager.commitChanges(); // rebuild layer hierarchy from scratch and verify that it matches the updated state. - LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers()); + LayerHierarchyBuilder newBuilder; + newBuilder.update(mLifecycleManager); EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), getTraversalPath(newBuilder.getHierarchy())); EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), @@ -512,10 +510,7 @@ protected: } void update(LayerSnapshotBuilder& snapshotBuilder) { - if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { - mHierarchyBuilder.update(mLifecycleManager.getLayers(), - mLifecycleManager.getDestroyedLayers()); - } + mHierarchyBuilder.update(mLifecycleManager); LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), .layerLifecycleManager = mLifecycleManager, .includeMetadata = false, @@ -530,7 +525,7 @@ protected: mLifecycleManager.commitChanges(); } - LayerHierarchyBuilder mHierarchyBuilder{{}}; + LayerHierarchyBuilder mHierarchyBuilder; DisplayInfos mFrontEndDisplayInfos; bool mHasDisplayChanges = false; diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 8a412f8509..110f324c8b 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -56,7 +56,7 @@ protected: LayerHistoryIntegrationTest() : LayerSnapshotTestBase() { mFlinger.resetScheduler(mScheduler); mLifecycleManager = {}; - mHierarchyBuilder = {{}}; + mHierarchyBuilder = {}; } void updateLayerSnapshotsAndLayerHistory(nsecs_t now) { @@ -165,12 +165,8 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; - - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); - TestableSurfaceFlinger mFlinger; + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 0ae3ca33b1..9b8ff42782 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -146,12 +146,8 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - - mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); - TestableSurfaceFlinger mFlinger; + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { @@ -650,7 +646,7 @@ TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) { // Second LayerRequirement is the frame rate specification EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote); EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[1].frameRateCategory); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory); // layer became inactive, but the vote stays setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index 07a522ac4e..c1fa6ac6f2 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -64,10 +64,8 @@ protected: HI_FPS)), DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); TestableSurfaceFlinger mFlinger; + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { @@ -215,7 +213,8 @@ TEST_F(LayerInfoTest, getRefreshRateVote_explicitVote) { TEST_F(LayerInfoTest, getRefreshRateVote_explicitVoteWithCategory) { LayerInfo::LayerVote vote = {.type = LayerHistory::LayerVoteType::ExplicitDefault, .fps = 20_Hz, - .category = FrameRateCategory::High}; + .category = FrameRateCategory::High, + .categorySmoothSwitchOnly = true}; layerInfo.setLayerVote(vote); auto actualVotes = @@ -223,10 +222,12 @@ TEST_F(LayerInfoTest, getRefreshRateVote_explicitVoteWithCategory) { ASSERT_EQ(actualVotes.size(), 2u); ASSERT_EQ(actualVotes[0].type, LayerHistory::LayerVoteType::ExplicitCategory); ASSERT_EQ(actualVotes[0].category, vote.category); + ASSERT_TRUE(actualVotes[0].categorySmoothSwitchOnly); ASSERT_EQ(actualVotes[1].type, vote.type); ASSERT_EQ(actualVotes[1].fps, vote.fps); ASSERT_EQ(actualVotes[1].seamlessness, vote.seamlessness); - ASSERT_EQ(actualVotes[1].category, vote.category); + ASSERT_EQ(actualVotes[1].category, FrameRateCategory::Default); + ASSERT_TRUE(actualVotes[1].categorySmoothSwitchOnly); } TEST_F(LayerInfoTest, getRefreshRateVote_explicitCategory) { diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 50cd784725..3baa48d002 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -58,13 +58,24 @@ protected: void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) { if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { - mHierarchyBuilder.update(mLifecycleManager.getLayers(), - mLifecycleManager.getDestroyedLayers()); + mHierarchyBuilder.update(mLifecycleManager); } args.root = mHierarchyBuilder.getHierarchy(); actualBuilder.update(args); } + void update(LayerSnapshotBuilder& actualBuilder) { + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + update(actualBuilder, args); + } + void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges, const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) { LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), @@ -354,6 +365,23 @@ TEST_F(LayerSnapshotTest, CanCropTouchableRegion) { EXPECT_EQ(getSnapshot({.id = 111})->inputInfo.touchableRegion.bounds(), modifiedTouchCrop); } +TEST_F(LayerSnapshotTest, CanCropTouchableRegionWithDisplayTransform) { + DisplayInfo displayInfo; + displayInfo.transform = ui::Transform(ui::Transform::RotationFlags::ROT_90, 1000, 1000); + mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), displayInfo); + + Rect touchCrop{300, 300, 400, 500}; + createRootLayer(3); + setCrop(3, touchCrop); + setLayerStack(3, 1); + Region touch{Rect{0, 0, 1000, 1000}}; + setTouchableRegionCrop(3, touch, /*touchCropId=*/3, /*replaceTouchableRegionWithCrop=*/false); + + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3}); + Rect rotatedCrop = {500, 300, 700, 400}; + EXPECT_EQ(getSnapshot({.id = 3})->inputInfo.touchableRegion.bounds(), rotatedCrop); +} + TEST_F(LayerSnapshotTest, blurUpdatesWhenAlphaChanges) { int blurRadius = 42; setBackgroundBlurRadius(1221, static_cast<uint32_t>(blurRadius)); @@ -651,7 +679,7 @@ TEST_F(LayerSnapshotTest, frameRateWithCategory) { // │ └── 13 // └── 2 setFrameRate(11, 244.f, 0, 0); - setFrameRateCategory(122, 3 /* Normal */); + setFrameRateCategory(122, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); // verify parent 1 gets no vote @@ -846,7 +874,7 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) { // │ │ └── 1221 // │ └── 13 // └── 2 - setFrameRateCategory(12, 4 /* high */); + setFrameRateCategory(12, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH); setFrameRate(122, 123.f, 0, 0); setFrameRateSelectionStrategy(12, 1 /* OverrideChildren */); @@ -888,7 +916,7 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) { // │ │ └── 1221 // │ └── 13 // └── 2 - setFrameRateCategory(12, 0 /* default */); + setFrameRateCategory(12, ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT); setFrameRateSelectionStrategy(12, 0 /* Default */); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); // verify parent 1 gets no vote @@ -1183,4 +1211,51 @@ TEST_F(LayerSnapshotTest, propagateDropInputMode) { EXPECT_EQ(getSnapshot(11)->dropInputMode, gui::DropInputMode::ALL); } +TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { + LayerHierarchyTestBase::createRootLayer(3); + setColor(3, {-1._hf, -1._hf, -1._hf}); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; + transactions.back().states.front().layerId = 3; + transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make(); + auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + inputInfo->token = sp<BBinder>::make(); + mLifecycleManager.applyTransactions(transactions); + + update(mSnapshotBuilder); + + bool foundInputLayer = false; + mSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) { + if (snapshot.uniqueSequence == 3) { + foundInputLayer = true; + } + }); + EXPECT_TRUE(foundInputLayer); +} + +TEST_F(LayerSnapshotTest, canOccludePresentation) { + setFlags(12, layer_state_t::eCanOccludePresentation, layer_state_t::eCanOccludePresentation); + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .displayChanges = false, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_EQ(getSnapshot(1)->inputInfo.canOccludePresentation, false); + + // ensure we can set the property on the window info for layer and all its children + EXPECT_EQ(getSnapshot(12)->inputInfo.canOccludePresentation, true); + EXPECT_EQ(getSnapshot(121)->inputInfo.canOccludePresentation, true); + EXPECT_EQ(getSnapshot(1221)->inputInfo.canOccludePresentation, true); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 9aa089f900..71f9f88ba7 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -25,6 +25,7 @@ #include "FrameTimeline.h" #include "Scheduler/MessageQueue.h" #include "mock/MockVSyncDispatch.h" +#include "utils/Timers.h" namespace android { @@ -41,6 +42,7 @@ struct NoOpCompositor final : ICompositor { return {}; } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) {} } gNoOpCompositor; class TestableMessageQueue : public impl::MessageQueue { @@ -48,6 +50,8 @@ class TestableMessageQueue : public impl::MessageQueue { using MessageQueue::Handler::Handler; MOCK_METHOD(void, dispatchFrame, (VsyncId, TimePoint), (override)); + MOCK_METHOD(bool, isFramePending, (), (const, override)); + MOCK_METHOD(TimePoint, getExpectedVsyncTime, (), (const override)); }; explicit TestableMessageQueue(sp<MockHandler> handler) @@ -72,7 +76,8 @@ struct MockTokenManager : frametimeline::TokenManager { struct MessageQueueTest : testing::Test { void SetUp() override { EXPECT_CALL(*mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, kDuration)); + EXPECT_NO_FATAL_FAILURE( + mEventQueue.initVsyncInternal(mVSyncDispatch, mTokenManager, kDuration)); EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1); } @@ -91,46 +96,62 @@ namespace { TEST_F(MessageQueueTest, commit) { const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = 0}; - EXPECT_FALSE(mEventQueue.getScheduledFrameTime()); + .lastVsync = 0}; + EXPECT_FALSE(mEventQueue.getScheduledFrameResult()); - EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); + const auto timePoint = TimePoint::fromNs(1234); + const auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint}; + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); - ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); - EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); + const auto scheduledFrameResult = mEventQueue.getScheduledFrameResult(); + ASSERT_TRUE(scheduledFrameResult); + EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns()); + EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns()); } TEST_F(MessageQueueTest, commitTwice) { InSequence s; const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = 0}; + .lastVsync = 0}; - EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); + auto timePoint = TimePoint::fromNs(1234); + auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint}; + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); - ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); - EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); + auto scheduledFrameResult = mEventQueue.getScheduledFrameResult(); + ASSERT_TRUE(scheduledFrameResult); + EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns()); + EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns()); - EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567)); + timePoint = TimePoint::fromNs(4567); + scheduleResult = scheduler::ScheduleResult{timePoint, timePoint}; + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); - ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); - EXPECT_EQ(4567, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); + scheduledFrameResult = mEventQueue.getScheduledFrameResult(); + ASSERT_TRUE(scheduledFrameResult); + EXPECT_EQ(4567, scheduledFrameResult->callbackTime.ns()); + EXPECT_EQ(4567, scheduledFrameResult->vsyncTime.ns()); } TEST_F(MessageQueueTest, commitTwiceWithCallback) { InSequence s; const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = 0}; + .lastVsync = 0}; - EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); + const auto timePoint = TimePoint::fromNs(1234); + auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint}; + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); - ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); - EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); + auto scheduledFrameResult = mEventQueue.getScheduledFrameResult(); + ASSERT_TRUE(scheduledFrameResult); + EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns()); + EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns()); constexpr TimePoint kStartTime = TimePoint::fromNs(100); constexpr TimePoint kEndTime = kStartTime + kDuration; @@ -146,14 +167,15 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { EXPECT_NO_FATAL_FAILURE( mEventQueue.vsyncCallback(kPresentTime.ns(), kStartTime.ns(), kEndTime.ns())); - EXPECT_FALSE(mEventQueue.getScheduledFrameTime()); + EXPECT_FALSE(mEventQueue.getScheduledFrameResult()); const auto timingAfterCallback = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(), .readyDuration = 0, - .earliestVsync = kPresentTime.ns()}; - - EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0)); + .lastVsync = kPresentTime.ns()}; + scheduleResult = scheduler::ScheduleResult{TimePoint::fromNs(0), TimePoint::fromNs(0)}; + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)) + .WillOnce(Return(scheduleResult)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); } @@ -163,11 +185,26 @@ TEST_F(MessageQueueTest, commitWithDurationChange) { const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDifferentDuration.ns(), .readyDuration = 0, - .earliestVsync = 0}; + .lastVsync = 0}; - EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0)); + const auto scheduleResult = + scheduler::ScheduleResult{TimePoint::fromNs(0), TimePoint::fromNs(0)}; + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); } +TEST_F(MessageQueueTest, scheduleResultWhenFrameIsPending) { + const auto timePoint = TimePoint::now(); + EXPECT_CALL(*mEventQueue.mHandler, isFramePending()).WillOnce(Return(true)); + EXPECT_CALL(*mEventQueue.mHandler, getExpectedVsyncTime()).WillRepeatedly(Return(timePoint)); + + const auto scheduledFrameResult = mEventQueue.getScheduledFrameResult(); + + ASSERT_TRUE(scheduledFrameResult); + EXPECT_NEAR(static_cast<double>(TimePoint::now().ns()), + static_cast<double>(scheduledFrameResult->callbackTime.ns()), ms2ns(1)); + EXPECT_EQ(timePoint, scheduledFrameResult->vsyncTime); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 0cacf810c0..0a6e3054dd 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -103,7 +103,7 @@ struct TestableRefreshRateSelector : RefreshRateSelector { auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; } auto getRankedFrameRates(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const { + GlobalSignals signals = {}) const { const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals); EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(), @@ -1607,6 +1607,92 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_12 } } +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighHint) { + auto selector = createSelector(makeModes(kMode24, kMode30, kMode60, kMode120), kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::NoVote; + lr2.name = "NoVote"; + auto actualRankedFrameRates = selector.getRankedFrameRates(layers); + // Gets touch boost + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); + + // No touch boost, for example a game that uses setFrameRate(30, default compatibility). + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + actualRankedFrameRates = selector.getRankedFrameRates(layers); + EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId30, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitCategory; + lr2.frameRateCategory = FrameRateCategory::HighHint; + lr2.name = "ExplicitCategory HighHint#2"; + actualRankedFrameRates = selector.getRankedFrameRates(layers); + // Gets touch boost + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitCategory; + lr2.frameRateCategory = FrameRateCategory::Low; + lr2.name = "ExplicitCategory Low"; + actualRankedFrameRates = selector.getRankedFrameRates(layers); + // Gets touch boost + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitExactOrMultiple"; + actualRankedFrameRates = selector.getRankedFrameRates(layers); + // Gets touch boost + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitExact; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitExact"; + actualRankedFrameRates = selector.getRankedFrameRates(layers); + if (selector.supportsAppFrameRateOverrideByContent()) { + // Gets touch boost + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, + actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); + } else { + EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId30, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); + } +} + TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_smoothSwitchOnly_60_120_nonVrr) { if (GetParam() != Config::FrameRateOverride::Enabled) { @@ -3073,6 +3159,210 @@ TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { EXPECT_TRUE(frameRateOverrides.empty()); } +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_withFrameRateCategory) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } + + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); + + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, + {.ownerUid = 1234, .weight = 1.f}}; + + // HighHint case with touch boost and thus should skip frame rate override. + layers[0].name = "ExplicitCategory HighHint"; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].desiredRefreshRate = 0_Hz; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[1].name = "ExplicitCategory High"; + layers[1].vote = LayerVoteType::ExplicitCategory; + layers[1].desiredRefreshRate = 0_Hz; + layers[1].frameRateCategory = FrameRateCategory::High; + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); + + // HighHint case with touch boost and thus should skip frame rate override. + layers[0].name = "ExplicitCategory HighHint"; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].desiredRefreshRate = 0_Hz; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[1].name = "ExplicitCategory Normal"; + layers[1].vote = LayerVoteType::ExplicitCategory; + layers[1].desiredRefreshRate = 0_Hz; + layers[1].frameRateCategory = FrameRateCategory::Normal; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); + + // HighHint case with touch boost and thus should skip frame rate override. + layers[0].name = "ExplicitCategory HighHint"; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].desiredRefreshRate = 0_Hz; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[1].name = "ExplicitCategory Low"; + layers[1].vote = LayerVoteType::ExplicitCategory; + layers[1].desiredRefreshRate = 0_Hz; + layers[1].frameRateCategory = FrameRateCategory::Low; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); + + // HighHint case with touch boost and thus should skip frame rate override. + layers[0].name = "ExplicitCategory HighHint"; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].desiredRefreshRate = 0_Hz; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[1].name = "ExplicitCategory NoPreference"; + layers[1].vote = LayerVoteType::ExplicitCategory; + layers[1].desiredRefreshRate = 0_Hz; + layers[1].frameRateCategory = FrameRateCategory::NoPreference; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); + + // HighHint case *without* touch boost has frame rate override. + // For example, game and touch interaction. + layers[0].name = "ExplicitCategory HighHint"; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].desiredRefreshRate = 0_Hz; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[1].name = "ExplicitDefault 60"; + layers[1].vote = LayerVoteType::ExplicitDefault; + layers[1].desiredRefreshRate = 60_Hz; + layers[1].frameRateCategory = FrameRateCategory::Default; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + // HighHint case with touch boost and thus should skip frame rate override. + layers[0].name = "ExplicitCategory HighHint"; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].desiredRefreshRate = 0_Hz; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[1].name = "ExplicitExactOrMultiple 30"; + layers[1].vote = LayerVoteType::ExplicitExactOrMultiple; + layers[1].desiredRefreshRate = 30_Hz; + layers[1].frameRateCategory = FrameRateCategory::Default; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); + + // HighHint case with touch boost and thus should skip frame rate override. + layers[0].name = "ExplicitCategory HighHint"; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].desiredRefreshRate = 0_Hz; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[1].name = "ExplicitExact 60"; + layers[1].vote = LayerVoteType::ExplicitExact; + layers[1].desiredRefreshRate = 60_Hz; + layers[1].frameRateCategory = FrameRateCategory::Default; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); + + // HighHint case with touch boost and thus should skip frame rate override. + layers[0].name = "ExplicitCategory HighHint"; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].desiredRefreshRate = 0_Hz; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[1].name = "ExplicitGte 60"; + layers[1].vote = LayerVoteType::ExplicitGte; + layers[1].desiredRefreshRate = 60_Hz; + layers[1].frameRateCategory = FrameRateCategory::Default; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); + + // ExplicitCategory case that expects no global touch boost and thus has frame rate override. + layers[0].name = "ExplicitDefault 60"; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].frameRateCategory = FrameRateCategory::Default; + layers[1].name = "ExplicitCategory High"; + layers[1].vote = LayerVoteType::ExplicitCategory; + layers[1].desiredRefreshRate = 0_Hz; + layers[1].frameRateCategory = FrameRateCategory::High; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(120_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(120_Hz, frameRateOverrides.at(1234)); + + // ExplicitCategory case that expects no global touch boost and thus has frame rate override. + layers[0].name = "ExplicitDefault 60"; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].frameRateCategory = FrameRateCategory::Default; + layers[1].name = "ExplicitCategory Normal"; + layers[1].vote = LayerVoteType::ExplicitCategory; + layers[1].desiredRefreshRate = 0_Hz; + layers[1].frameRateCategory = FrameRateCategory::Normal; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + // ExplicitCategory case that expects no global touch boost and thus has frame rate override. + layers[0].name = "ExplicitDefault 60"; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].frameRateCategory = FrameRateCategory::Default; + layers[1].name = "ExplicitCategory Low"; + layers[1].vote = LayerVoteType::ExplicitCategory; + layers[1].desiredRefreshRate = 0_Hz; + layers[1].frameRateCategory = FrameRateCategory::Low; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + // ExplicitCategory case that expects no global touch boost and thus has frame rate override. + layers[0].name = "ExplicitDefault 60"; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].frameRateCategory = FrameRateCategory::Default; + layers[1].name = "ExplicitCategory NoPreference"; + layers[1].vote = LayerVoteType::ExplicitCategory; + layers[1].desiredRefreshRate = 0_Hz; + layers[1].frameRateCategory = FrameRateCategory::NoPreference; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); +} + TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) { if (GetParam() == Config::FrameRateOverride::Disabled) { return; @@ -3118,6 +3408,17 @@ TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) { frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); EXPECT_TRUE(frameRateOverrides.empty()); + + layers[0].vote = LayerVoteType::ExplicitGte; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); } TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) { diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index e2e3d7b12b..fba77e997c 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -37,7 +37,7 @@ protected: ~RefreshRateStatsTest(); void resetStats(Fps fps) { - mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, fps, PowerMode::OFF); + mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, fps); } mock::TimeStats mTimeStats; diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index e515895a1a..049b0923a6 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" @@ -95,16 +96,12 @@ protected: kDisplay1Mode60->getId()); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableScheduler* mScheduler = - new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback}; - surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}}; + TestableSurfaceFlinger mFlinger; + TestableScheduler* mScheduler = new TestableScheduler{mSelector, mFlinger, mSchedulerCallback}; + surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder; - ConnectionHandle mConnectionHandle; MockEventThread* mEventThread; sp<MockEventThreadConnection> mEventThreadConnection; - - TestableSurfaceFlinger mFlinger; }; SchedulerTest::SchedulerTest() { @@ -119,54 +116,13 @@ SchedulerTest::SchedulerTest() { EXPECT_CALL(*mEventThread, createEventConnection(_, _)) .WillRepeatedly(Return(mEventThreadConnection)); - mConnectionHandle = mScheduler->createConnection(std::move(eventThread)); - EXPECT_TRUE(mConnectionHandle); + mScheduler->setEventThread(Cycle::Render, std::move(eventThread)); mFlinger.resetScheduler(mScheduler); } } // namespace -TEST_F(SchedulerTest, invalidConnectionHandle) { - ConnectionHandle handle; - - const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle); - - EXPECT_FALSE(connection); - EXPECT_FALSE(mScheduler->getEventConnection(handle)); - - // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads. - EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0); - mScheduler->onHotplugReceived(handle, kDisplayId1, false); - - std::string output; - EXPECT_CALL(*mEventThread, dump(_)).Times(0); - mScheduler->dump(handle, output); - EXPECT_TRUE(output.empty()); - - EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0); - mScheduler->setDuration(handle, 10ns, 20ns); -} - -TEST_F(SchedulerTest, validConnectionHandle) { - const sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle); - - ASSERT_EQ(mEventThreadConnection, connection); - EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle)); - - EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1); - mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false); - - std::string output("dump"); - EXPECT_CALL(*mEventThread, dump(output)).Times(1); - mScheduler->dump(mConnectionHandle, output); - EXPECT_FALSE(output.empty()); - - EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1); - mScheduler->setDuration(mConnectionHandle, 10ns, 20ns); -} - TEST_F(SchedulerTest, registerDisplay) FTL_FAKE_GUARD(kMainThreadContext) { // Hardware VSYNC should not change if the display is already registered. EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId1, false)).Times(0); @@ -238,22 +194,6 @@ TEST_F(SchedulerTest, dispatchCachedReportedMode) { EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode()); } -TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) { - const auto mode = DisplayMode::Builder(hal::HWConfigId(0)) - .setId(DisplayModeId(111)) - .setPhysicalDisplayId(kDisplayId1) - .setVsyncPeriod(111111) - .build(); - - // If the handle is incorrect, the function should return before - // onModeChange is called. - ConnectionHandle invalidHandle = {.id = 123}; - EXPECT_NO_FATAL_FAILURE( - mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, - {90_Hz, ftl::as_non_null(mode)})); - EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); -} - TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms)); EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms)); @@ -342,6 +282,61 @@ TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals)); } +TEST_F(SchedulerTest, chooseDisplayModesSingleDisplayHighHintTouchSignal) { + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); + + using DisplayModeChoice = TestableScheduler::DisplayModeChoice; + + std::vector<RefreshRateSelector::LayerRequirement> layers = + std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}}); + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + // Scenario that is similar to game. Expects no touch boost. + lr1.vote = RefreshRateSelector::LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = RefreshRateSelector::LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + mScheduler->setContentRequirements(layers); + auto modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + auto choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, {.touch = false})); + + // Scenario that is similar to video playback and interaction. Expects touch boost. + lr1.vote = RefreshRateSelector::LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = RefreshRateSelector::LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitExactOrMultiple"; + mScheduler->setContentRequirements(layers); + modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, {.touch = true})); + + // Scenario with explicit category and HighHint. Expects touch boost. + lr1.vote = RefreshRateSelector::LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = RefreshRateSelector::LayerVoteType::ExplicitCategory; + lr2.frameRateCategory = FrameRateCategory::Low; + lr2.name = "ExplicitCategory Low"; + mScheduler->setContentRequirements(layers); + modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, {.touch = true})); +} + TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { mScheduler->registerDisplay(kDisplayId1, std::make_shared<RefreshRateSelector>(kDisplay1Modes, @@ -517,6 +512,7 @@ TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) { } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {} } compositor(*mScheduler); mScheduler->doFrameSignal(compositor, VsyncId(42)); @@ -568,44 +564,50 @@ 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, - kOutlierTolerancePercent, mVsyncTrackerCallback); + std::make_shared<VSyncPredictor>(std::make_unique<SystemClock>(), kMode, kHistorySize, + kMinimumSamplesForPrediction, + kOutlierTolerancePercent); std::shared_ptr<RefreshRateSelector> vrrSelectorPtr = std::make_shared<RefreshRateSelector>(makeModes(kMode), kMode->getId()); TestableScheduler scheduler{std::make_unique<android::mock::VsyncController>(), vrrTracker, vrrSelectorPtr, - sp<VsyncModulator>::make(VsyncConfigSet{}), - mSchedulerCallback, - mVsyncTrackerCallback}; + mFlinger.getFactory(), + mFlinger.getTimeStats(), + mSchedulerCallback}; scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker); vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate); vrrTracker->addVsyncTimestamp(0); - - // Next frame at refresh rate as no previous frame - EXPECT_EQ(refreshRate, - scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), TimePoint::fromNs(0))); + // Set 1000 as vsync seq #0 + vrrTracker->nextAnticipatedVSyncTimeFrom(700); EXPECT_EQ(Fps::fromPeriodNsecs(1000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(500))); + TimePoint::fromNs(1000))); EXPECT_EQ(Fps::fromPeriodNsecs(1000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(1500))); + TimePoint::fromNs(2000))); + // Not crossing the min frame period + 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); + // Set 2000 as vsync seq #0 + vrrTracker->nextAnticipatedVSyncTimeFrom(1700); + EXPECT_EQ(Fps::fromPeriodNsecs(2000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(2500))); + TimePoint::fromNs(2000))); EXPECT_EQ(Fps::fromPeriodNsecs(2000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(4500))); + TimePoint::fromNs(4000))); } TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) { @@ -700,7 +702,7 @@ TEST_F(AttachedChoreographerTest, registerSingle) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); const sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size()); ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence())); @@ -729,9 +731,9 @@ TEST_F(AttachedChoreographerTest, registerMultipleOnSameLayer) { .WillOnce(Return(mockConnection2)); const sp<IDisplayEventConnection> connection1 = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, handle); const sp<IDisplayEventConnection> connection2 = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, handle); EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size()); ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence())); @@ -749,9 +751,9 @@ TEST_F(AttachedChoreographerTest, registerMultipleOnDifferentLayers) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2); const sp<IDisplayEventConnection> connection1 = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer1->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer1->getHandle()); const sp<IDisplayEventConnection> connection2 = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer2->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer2->getHandle()); EXPECT_EQ(2u, mScheduler->mutableAttachedChoreographers().size()); @@ -778,7 +780,7 @@ TEST_F(AttachedChoreographerTest, removedWhenConnectionIsGone) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence())); EXPECT_EQ(1u, @@ -808,7 +810,7 @@ TEST_F(AttachedChoreographerTest, removedWhenLayerIsGone) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); const sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); layer.clear(); mFlinger.mutableLayersPendingRemoval().clear(); @@ -822,7 +824,7 @@ void AttachedChoreographerTest::frameRateTestScenario(Fps layerFps, int8_t frame EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); RequestedLayerState layerState(LayerCreationArgs(layer->getSequence())); LayerHierarchy hierarchy(&layerState); @@ -882,7 +884,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateParent) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); @@ -909,7 +911,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateParent2Children) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); @@ -944,7 +946,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateParentConflictingChildren) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); @@ -978,7 +980,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateChild) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); @@ -1004,7 +1006,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateChildNotOverriddenByParent) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp<IDisplayEventConnection> connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp deleted file mode 100644 index 45b761036a..0000000000 --- a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include "Scheduler/StrongTyping.h" - -using namespace testing; - -namespace android { - -TEST(StrongTypeTest, comparison) { - using SpunkyType = StrongTyping<int, struct SpunkyTypeTag, Compare>; - SpunkyType f1(10); - - EXPECT_TRUE(f1 == f1); - EXPECT_TRUE(SpunkyType(10) != SpunkyType(11)); - EXPECT_FALSE(SpunkyType(31) != SpunkyType(31)); - - EXPECT_TRUE(SpunkyType(10) < SpunkyType(11)); - EXPECT_TRUE(SpunkyType(-1) < SpunkyType(0)); - EXPECT_FALSE(SpunkyType(-10) < SpunkyType(-20)); - - EXPECT_TRUE(SpunkyType(10) <= SpunkyType(11)); - EXPECT_TRUE(SpunkyType(10) <= SpunkyType(10)); - EXPECT_TRUE(SpunkyType(-10) <= SpunkyType(1)); - EXPECT_FALSE(SpunkyType(10) <= SpunkyType(9)); - - EXPECT_TRUE(SpunkyType(11) >= SpunkyType(11)); - EXPECT_TRUE(SpunkyType(12) >= SpunkyType(11)); - EXPECT_FALSE(SpunkyType(11) >= SpunkyType(12)); - - EXPECT_FALSE(SpunkyType(11) > SpunkyType(12)); - EXPECT_TRUE(SpunkyType(-11) < SpunkyType(7)); -} - -TEST(StrongTypeTest, addition) { - using FunkyType = StrongTyping<int, struct FunkyTypeTag, Compare, Add>; - FunkyType f2(22); - FunkyType f1(10); - - EXPECT_THAT(f1 + f2, Eq(FunkyType(32))); - EXPECT_THAT(f2 + f1, Eq(FunkyType(32))); - - EXPECT_THAT(++f1.value(), Eq(11)); - EXPECT_THAT(f1.value(), Eq(11)); - EXPECT_THAT(f1++.value(), Eq(11)); - EXPECT_THAT(f1++.value(), Eq(12)); - EXPECT_THAT(f1.value(), Eq(13)); - - auto f3 = f1; - EXPECT_THAT(f1, Eq(f3)); - EXPECT_THAT(f1, Lt(f2)); - - f3 += f1; - EXPECT_THAT(f1.value(), Eq(13)); - EXPECT_THAT(f3.value(), Eq(26)); -} -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp new file mode 100644 index 0000000000..f127213f0d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp @@ -0,0 +1,63 @@ +/* + * 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 "LibSurfaceFlingerUnittests" + +#include "CommitAndCompositeTest.h" + +#define EXPECT_COLOR_MATRIX_CHANGED(current, drawing) \ + EXPECT_EQ(current, mFlinger.currentState().colorMatrixChanged); \ + EXPECT_EQ(drawing, mFlinger.drawingState().colorMatrixChanged); + +namespace android { + +class ColorMatrixTest : public CommitAndCompositeTest {}; + +TEST_F(ColorMatrixTest, colorMatrixChanged) { + EXPECT_COLOR_MATRIX_CHANGED(true, true); + mFlinger.mutableTransactionFlags() |= eTransactionNeeded; + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); + + mFlinger.setDaltonizerType(ColorBlindnessType::Deuteranomaly); + EXPECT_COLOR_MATRIX_CHANGED(true, false); + + mFlinger.commit(); + EXPECT_COLOR_MATRIX_CHANGED(false, true); + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); +} + +TEST_F(ColorMatrixTest, colorMatrixChangedAfterDisplayTransaction) { + EXPECT_COLOR_MATRIX_CHANGED(true, true); + mFlinger.mutableTransactionFlags() |= eTransactionNeeded; + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); + + mFlinger.createDisplay(String8("Test Display"), false); + + mFlinger.commit(); + EXPECT_COLOR_MATRIX_CHANGED(false, true); + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 7ad97a2133..15a6db626a 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -21,9 +21,21 @@ #include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockDisplayModeSpecs.h" +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include <ftl/fake_guard.h> #include <scheduler/Fps.h> +using namespace com::android::graphics::surfaceflinger; + +#define EXPECT_SET_ACTIVE_CONFIG(displayId, modeId) \ + EXPECT_CALL(*mComposer, \ + setActiveConfigWithConstraints(displayId, \ + static_cast<hal::HWConfigId>( \ + ftl::to_underlying(modeId)), \ + _, _)) \ + .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))) + namespace android { namespace { @@ -53,7 +65,7 @@ public: auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_shared<mock::VSyncTracker>(); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return( TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); @@ -138,14 +150,14 @@ void DisplayModeSwitchingTest::setupScheduler( auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_shared<mock::VSyncTracker>(); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly( Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); EXPECT_CALL(*vsyncTracker, minFramePeriod()) .WillRepeatedly(Return(Period::fromNs( TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD))); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), std::move(selectorPtr), @@ -161,8 +173,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequ mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), false, 0, - 120)); + mock::createDisplayModeSpecs(kModeId90, false, 0, 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90); @@ -170,10 +181,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequ // Verify that next commit will call setActiveConfigWithConstraints in HWC const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); mFlinger.commit(); @@ -202,8 +210,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshR mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), true, 0, - 120)); + mock::createDisplayModeSpecs(kModeId90, true, 0, 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90); @@ -212,10 +219,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshR // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); EXPECT_CALL(*mAppEventThread, onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)})); @@ -238,28 +242,20 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), false, 0, - 120)); + mock::createDisplayModeSpecs(kModeId90, false, 0, 120)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); mFlinger.commit(); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId120.value(), false, 0, - 180)); + mock::createDisplayModeSpecs(kModeId120, false, 0, 180)); ASSERT_TRUE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120); - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId120.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId120); mFlinger.commit(); @@ -281,8 +277,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRe mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0, - 120)); + mock::createDisplayModeSpecs(kModeId90_4K, false, 0, 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90_4K); @@ -291,10 +286,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRe // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId90_4K.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90_4K); EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true)); @@ -331,7 +323,7 @@ MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { } if (arg->getDesiredMode()->mode.modePtr->getId() != modeId) { - *result_listener << "Unexpected desired mode " << modeId; + *result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId); return false; } @@ -345,14 +337,15 @@ MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { MATCHER_P(ModeSettledTo, modeId, "") { if (const auto desiredOpt = arg->getDesiredMode()) { - *result_listener << "Unsettled desired mode " << desiredOpt->mode.modePtr->getId(); + *result_listener << "Unsettled desired mode " + << ftl::to_underlying(desiredOpt->mode.modePtr->getId()); return false; } ftl::FakeGuard guard(kMainThreadContext); if (arg->getActiveMode().modePtr->getId() != modeId) { - *result_listener << "Settled to unexpected active mode " << modeId; + *result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId); return false; } @@ -360,6 +353,13 @@ MATCHER_P(ModeSettledTo, modeId, "") { } TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); + const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -376,22 +376,19 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, false, + 0.f, 120.f))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, false, + 0.f, 120.f))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kInnerDisplayHwcId, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); mFlinger.commit(); @@ -412,10 +409,7 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kOuterDisplayHwcId, - hal::HWConfigId(kModeId60.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60); mFlinger.commit(); @@ -429,6 +423,12 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { } TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -447,27 +447,20 @@ TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, false, + 0.f, 120.f))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, false, + 0.f, 120.f))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kInnerDisplayHwcId, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); - - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kOuterDisplayHwcId, - hal::HWConfigId(kModeId60.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); + EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60); mFlinger.commit(); @@ -486,8 +479,8 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, false, + 0.f, 120.f))); EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); @@ -495,10 +488,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { mDisplay->setPowerMode(hal::PowerMode::OFF); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kInnerDisplayHwcId, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); mFlinger.commit(); @@ -512,6 +502,13 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { } TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); + const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -530,13 +527,13 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, false, + 0.f, 120.f))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, false, + 0.f, 120.f))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); @@ -545,10 +542,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { outerDisplay->setPowerMode(hal::PowerMode::OFF); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kInnerDisplayHwcId, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); mFlinger.commit(); @@ -567,10 +561,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { // Only the outer display is powered on. mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay); - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kOuterDisplayHwcId, - hal::HWConfigId(kModeId60.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60); mFlinger.commit(); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp index 41d8f9e4e0..19f8debe84 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp @@ -17,9 +17,9 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include "DisplayTransactionTestHelpers.h" #include <com_android_graphics_surfaceflinger_flags.h> #include <common/test/FlagUtils.h> +#include "DualDisplayTransactionTest.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -29,35 +29,9 @@ using namespace com::android::graphics::surfaceflinger; namespace android { namespace { -struct FoldableTest : DisplayTransactionTest { - static constexpr bool kWithMockScheduler = false; - FoldableTest() : DisplayTransactionTest(kWithMockScheduler) {} - - void SetUp() override { - injectMockScheduler(kInnerDisplayId); - - // Inject inner and outer displays with uninitialized power modes. - constexpr bool kInitPowerMode = false; - { - InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); - auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this); - injector.setPowerMode(std::nullopt); - injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()); - mInnerDisplay = injector.inject(); - } - { - OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); - auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this); - injector.setPowerMode(std::nullopt); - mOuterDisplay = injector.inject(); - } - } - - static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get(); - static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get(); - - sp<DisplayDevice> mInnerDisplay, mOuterDisplay; -}; +constexpr bool kExpectSetPowerModeOnce = false; +struct FoldableTest : DualDisplayTransactionTest<hal::PowerMode::OFF, hal::PowerMode::OFF, + kExpectSetPowerModeOnce> {}; TEST_F(FoldableTest, promotesPacesetterOnBoot) { // When the device boots, the inner display should be the pacesetter. diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp index 29acfaa1a5..4e9fba7bda 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp @@ -17,79 +17,16 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlingerGetDisplayStatsTest" -#include <compositionengine/Display.h> -#include <compositionengine/mock/DisplaySurface.h> #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <renderengine/mock/RenderEngine.h> #include <ui/DisplayStatInfo.h> -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockTimeStats.h" -#include "mock/system/window/MockNativeWindow.h" -using namespace android; -using namespace testing; +#include "CommitAndCompositeTest.h" namespace android { namespace { -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; -using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; -constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); -constexpr int DEFAULT_DISPLAY_WIDTH = 1920; -constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; - -class SurfaceFlingerGetDisplayStatsTest : public Test { -public: - void SetUp() override; - -protected: - TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); - sp<DisplayDevice> mDisplay; - sp<compositionengine::mock::DisplaySurface> mDisplaySurface = - sp<compositionengine::mock::DisplaySurface>::make(); - sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); - mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; - Hwc2::mock::Composer* mComposer = nullptr; -}; - -void SurfaceFlingerGetDisplayStatsTest::SetUp() { - mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); - mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); - mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); - mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); - mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); - static constexpr bool kIsPrimary = true; - FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) - .setPowerMode(hal::PowerMode::ON) - .inject(&mFlinger, mComposer); - auto compostionEngineDisplayArgs = - compositionengine::DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) - .setPowerAdvisor(mPowerAdvisor) - .setName("injected display") - .build(); - auto compositionDisplay = - compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), - std::move(compostionEngineDisplayArgs)); - mDisplay = - FakeDisplayDeviceInjector(mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary) - .setDisplaySurface(mDisplaySurface) - .setNativeWindow(mNativeWindow) - .setPowerMode(hal::PowerMode::ON) - .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) - .skipRegisterDisplay() - .inject(); -} +struct SurfaceFlingerGetDisplayStatsTest : CommitAndCompositeTest {}; // TODO (b/277364366): Clients should be updated to pass in the display they want. TEST_F(SurfaceFlingerGetDisplayStatsTest, nullptrSucceeds) { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp index a270dc91f2..897f9a0319 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp @@ -17,8 +17,14 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h> +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include "DisplayTransactionTestHelpers.h" +using namespace com::android::graphics::surfaceflinger; +using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent; + namespace android { class HotplugTest : public DisplayTransactionTest {}; @@ -87,6 +93,8 @@ TEST_F(HotplugTest, ignoresDuplicateDisconnection) { } TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + // Inject a primary display. PrimaryDisplayVariant::injectHwcDisplay(this); @@ -94,6 +102,10 @@ TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { constexpr bool kFailedHotplug = true; ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this); + EXPECT_CALL(*mEventThread, + onHotplugConnectionError(static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN))) + .Times(1); + // Simulate a connect event that fails to load display modes due to HWC already having // disconnected the display but SF yet having to process the queued disconnect event. EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _)) diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp index fc5f2b0b90..eaf468432c 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp @@ -17,66 +17,49 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include "DisplayTransactionTestHelpers.h" +#include "DualDisplayTransactionTest.h" namespace android { namespace { -class InitializeDisplaysTest : public DisplayTransactionTest {}; +constexpr bool kExpectSetPowerModeOnce = false; +struct InitializeDisplaysTest : DualDisplayTransactionTest<hal::PowerMode::OFF, hal::PowerMode::OFF, + kExpectSetPowerModeOnce> {}; -TEST_F(InitializeDisplaysTest, commitsPrimaryDisplay) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A primary display is set up - Case::Display::injectHwcDisplay(this); - auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this); - primaryDisplay.inject(); - - // -------------------------------------------------------------------- - // Call Expectations - - // We expect a call to get the active display config. - Case::Display::setupHwcGetActiveConfigCallExpectations(this); - - // We expect a scheduled commit for the display transaction. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); +TEST_F(InitializeDisplaysTest, initializesDisplays) { + // Scheduled by the display transaction, and by powering on each display. + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(3); EXPECT_CALL(static_cast<mock::VSyncTracker&>( mFlinger.scheduler()->getVsyncSchedule()->getTracker()), - nextAnticipatedVSyncTimeFrom(_)) + nextAnticipatedVSyncTimeFrom(_, _)) .WillRepeatedly(Return(0)); - // -------------------------------------------------------------------- - // Invocation - FTL_FAKE_GUARD(kMainThreadContext, mFlinger.initializeDisplays()); - // -------------------------------------------------------------------- - // Postconditions + for (const auto& display : {mInnerDisplay, mOuterDisplay}) { + const auto token = display->getDisplayToken().promote(); + ASSERT_TRUE(token); + + ASSERT_TRUE(hasCurrentDisplayState(token)); + const auto& state = getCurrentDisplayState(token); - // The primary display should have a current state - ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token())); - const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token()); + const ui::LayerStack expectedLayerStack = display == mInnerDisplay + ? ui::DEFAULT_LAYER_STACK + : ui::LayerStack::fromValue(ui::DEFAULT_LAYER_STACK.id + 1); - // The primary display state should be reset - EXPECT_EQ(ui::DEFAULT_LAYER_STACK, primaryDisplayState.layerStack); - EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation); - EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect); - EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect); + EXPECT_EQ(expectedLayerStack, state.layerStack); + EXPECT_EQ(ui::ROTATION_0, state.orientation); + EXPECT_EQ(Rect::INVALID_RECT, state.orientedDisplaySpaceRect); + EXPECT_EQ(Rect::INVALID_RECT, state.layerStackSpaceRect); - // The width and height should both be zero - EXPECT_EQ(0u, primaryDisplayState.width); - EXPECT_EQ(0u, primaryDisplayState.height); + EXPECT_EQ(0u, state.width); + EXPECT_EQ(0u, state.height); - // The display should be set to PowerMode::ON - ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token())); - auto displayDevice = primaryDisplay.mutableDisplayDevice(); - EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode()); + ASSERT_TRUE(hasDisplayDevice(token)); + EXPECT_EQ(PowerMode::ON, getDisplayDevice(token).getPowerMode()); + } - // The display transaction needed flag should be set. EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp index 7206e2977d..20a3315169 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp @@ -17,31 +17,108 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <gui/SurfaceComposerClient.h> #include "DisplayTransactionTestHelpers.h" namespace android { using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using android::hardware::graphics::composer::V2_1::Error; class NotifyExpectedPresentTest : public DisplayTransactionTest { public: void SetUp() override { - mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); - FakeHwcDisplayInjector(mDisplay->getPhysicalId(), hal::DisplayType::PHYSICAL, kIsPrimary) + const auto display = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); + mPhysicalDisplayId = display->getPhysicalId(); + FakeHwcDisplayInjector(mPhysicalDisplayId, hal::DisplayType::PHYSICAL, /*isPrimary=*/true) .setPowerMode(hal::PowerMode::ON) .inject(&mFlinger, mComposer); + + ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, + TimePoint::fromNs(0), + kFps60Hz)); + mCompositor = std::make_unique<Compositor>(mPhysicalDisplayId, mFlinger); } protected: - sp<DisplayDevice> mDisplay; - static constexpr bool kIsPrimary = true; - static constexpr hal::HWDisplayId HWC_DISPLAY_ID = - FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -}; + void setTransactionState() { + ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); + TransactionInfo transaction; + mFlinger.setTransactionState(FrameTimelineInfo{}, transaction.states, transaction.displays, + transaction.flags, transaction.applyToken, + transaction.inputWindowCommands, + TimePoint::now().ns() + s2ns(1), transaction.isAutoTimestamp, + transaction.unCachedBuffers, + /*HasListenerCallbacks=*/false, transaction.callbacks, + transaction.id, transaction.mergedTransactionIds); + } -TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { - const auto physicDisplayId = mDisplay->getPhysicalId(); - auto expectedPresentTime = systemTime() + ms2ns(10); + struct TransactionInfo { + Vector<ComposerState> states; + Vector<DisplayState> displays; + uint32_t flags = 0; + sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); + InputWindowCommands inputWindowCommands; + int64_t desiredPresentTime = 0; + bool isAutoTimestamp = false; + FrameTimelineInfo frameTimelineInfo{}; + std::vector<client_cache_t> unCachedBuffers; + uint64_t id = static_cast<uint64_t>(-1); + std::vector<uint64_t> mergedTransactionIds; + std::vector<ListenerCallbacks> callbacks; + }; + + struct Compositor final : ICompositor { + explicit Compositor(PhysicalDisplayId displayId, TestableSurfaceFlinger& surfaceFlinger) + : displayId(displayId), surfaceFlinger(surfaceFlinger) {} + + void sendNotifyExpectedPresentHint(PhysicalDisplayId id) override { + surfaceFlinger.sendNotifyExpectedPresentHint(id); + } + + bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { + return committed; + } + + CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId, + const scheduler::FrameTargeters& targeters) override { + pacesetterIds.composite = pacesetterId; + CompositeResultsPerDisplay results; + + for (const auto& [id, targeter] : targeters) { + vsyncIds.composite.emplace_back(id, targeter->target().vsyncId()); + surfaceFlinger.resetNotifyExpectedPresentHintState(pacesetterId); + results.try_emplace(id, + CompositeResult{.compositionCoverage = + CompositionCoverage::Hwc}); + } + + return results; + } + + void sample() override {} + void configure() override {} + + struct { + PhysicalDisplayId commit; + PhysicalDisplayId composite; + } pacesetterIds; + + using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>; + struct { + VsyncIds commit; + VsyncIds composite; + } vsyncIds; + + bool committed = true; + PhysicalDisplayId displayId; + TestableSurfaceFlinger& surfaceFlinger; + }; + + PhysicalDisplayId mPhysicalDisplayId; + std::unique_ptr<Compositor> mCompositor; + static constexpr hal::HWDisplayId kHwcDisplayId = + FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; static constexpr Fps kFps60Hz = 60_Hz; static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.getPeriodNsecs(); @@ -49,89 +126,199 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { static constexpr Period kVsyncPeriod = Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); - static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0); +}; - ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId, - kLastExpectedPresentTimestamp, - kFps60Hz)); +TEST_F(NotifyExpectedPresentTest, noNotifyExpectedPresentHintCall_absentTimeout) { + auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10); + ASSERT_NO_FATAL_FAILURE( + mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, + TimePoint::fromNs(expectedPresentTime), + kFps60Hz)); + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + for (int i = 0; i < 5; i++) { + expectedPresentTime += 2 * kFrameInterval5HzNs; + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + /*timeoutOpt*/ std::nullopt); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + } +} +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentHint_zeroTimeout) { + auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10); { - // Very first ExpectedPresent after idle, no previous timestamp + // Very first ExpectedPresent after idle, no previous timestamp. EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + + // Present frame + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Present happens and NotifyExpectedPresentHintStatus is start. + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { - // Absent timeoutNs - expectedPresentTime += 2 * kFrameInterval5HzNs; - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mCompositor->committed = false; + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(static_cast<mock::VSyncTracker&>( + mFlinger.scheduler()->getVsyncSchedule()->getTracker()), + nextAnticipatedVSyncTimeFrom(_, _)) + .WillRepeatedly(Return(expectedPresentTime)); + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, - /*timeoutOpt*/ std::nullopt); + Period::fromNs(0)); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Hint sent + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } { - // Timeout is 0 expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(static_cast<mock::VSyncTracker&>( + mFlinger.scheduler()->getVsyncSchedule()->getTracker()), + nextAnticipatedVSyncTimeFrom(_, _)) + .WillRepeatedly(Return(expectedPresentTime)); EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, Period::fromNs(0)); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + // Hint is executed + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + } +} +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { + auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10); + { + // Very first ExpectedPresent after idle, no previous timestamp + mCompositor->committed = false; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } { - // ExpectedPresent is after the timeoutNs + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + expectedPresentTime += 2 * kFrameInterval5HzNs; + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId)); + { + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + // Hint sent with the setTransactionState + setTransactionState(); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + } + } + { + // ExpectedPresentTime is after the timeoutNs + mCompositor->committed = true; + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Present happens notifyExpectedPresentHintStatus is Start + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + + // Another expectedPresent after timeout expectedPresentTime += 2 * kFrameInterval5HzNs; EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent has not changed - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { - // ExpectedPresent is after the last reported ExpectedPresent. + // ExpectedPresent is after the last reported ExpectedPresent and within timeout. expectedPresentTime += kFrameInterval60HzNs; - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, // representing we changed our decision and want to present earlier than previously // reported. + mCompositor->committed = false; expectedPresentTime -= kFrameInterval120HzNs; EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } } TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { - const auto physicDisplayId = mDisplay->getPhysicalId(); - const auto now = systemTime(); + const auto now = TimePoint::now().ns(); auto expectedPresentTime = now; static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); - ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId, + ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, TimePoint::fromNs(now), Fps::fromValue(0))); static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); @@ -147,7 +334,7 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { struct FrameRateIntervalTestData { int32_t frameIntervalNs; - bool callExpectedPresent; + bool callNotifyExpectedPresentHint; }; const std::vector<FrameRateIntervalTestData> frameIntervals = { {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, @@ -159,21 +346,39 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, }; - for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { - { - expectedPresentTime += frameIntervalNs; - if (callExpectedPresent) { - EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - frameIntervalNs)) - .WillOnce(Return(Error::NONE)); - } else { - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - } - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), - Fps::fromPeriodNsecs(frameIntervalNs), - kTimeoutNs); + for (size_t i = 0; i < frameIntervals.size(); i++) { + const auto& [frameIntervalNs, callNotifyExpectedPresentHint] = frameIntervals[i]; + expectedPresentTime += frameIntervalNs; + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), + Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); + + EXPECT_CALL(static_cast<mock::VSyncTracker&>( + mFlinger.scheduler()->getVsyncSchedule()->getTracker()), + nextAnticipatedVSyncTimeFrom(_, _)) + .WillRepeatedly(Return(expectedPresentTime)); + if (callNotifyExpectedPresentHint) { + mCompositor->committed = false; + ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId)) + << "Hint not scheduled for frameInterval " << frameIntervalNs << " at index " + << i; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + .WillOnce(Return(Error::NONE)); + } else { + // Only lastExpectedPresentTime is updated + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)) + << "LastExpectedPresentTime for frameInterval " << frameIntervalNs + << "at index " << i << " did not match for frameInterval " << frameIntervalNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + } + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + + if (callNotifyExpectedPresentHint) { + // Present resumes the calls to the notifyExpectedPresentHint. + mCompositor->committed = true; + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); } } } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index b80cb6625f..c3934e64f8 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -17,84 +17,18 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlingerPowerHintTest" -#include <compositionengine/Display.h> -#include <compositionengine/mock/DisplaySurface.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <renderengine/mock/RenderEngine.h> -#include <algorithm> #include <chrono> -#include <memory> -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockTimeStats.h" -#include "mock/system/window/MockNativeWindow.h" -using namespace android; -using namespace android::Hwc2::mock; -using namespace android::hardware::power; +#include "CommitAndCompositeTest.h" + using namespace std::chrono_literals; -using namespace testing; +using testing::_; +using testing::Return; namespace android { namespace { -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; -using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; - -constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); -constexpr int DEFAULT_DISPLAY_WIDTH = 1920; -constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; - -class SurfaceFlingerPowerHintTest : public Test { -public: - void SetUp() override; -protected: - TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); - sp<DisplayDevice> mDisplay; - sp<compositionengine::mock::DisplaySurface> mDisplaySurface = - sp<compositionengine::mock::DisplaySurface>::make(); - sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); - mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; - Hwc2::mock::Composer* mComposer = nullptr; -}; - -void SurfaceFlingerPowerHintTest::SetUp() { - mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); - mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); - mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); - mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); - mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); - static constexpr bool kIsPrimary = true; - FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) - .setPowerMode(hal::PowerMode::ON) - .inject(&mFlinger, mComposer); - auto compostionEngineDisplayArgs = - compositionengine::DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) - .setPowerAdvisor(mPowerAdvisor) - .setName("injected display") - .build(); - auto compositionDisplay = - compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), - std::move(compostionEngineDisplayArgs)); - mDisplay = - FakeDisplayDeviceInjector(mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary) - .setDisplaySurface(mDisplaySurface) - .setNativeWindow(mNativeWindow) - .setPowerMode(hal::PowerMode::ON) - .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) - .skipRegisterDisplay() - .inject(); -} +class SurfaceFlingerPowerHintTest : public CommitAndCompositeTest {}; TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true)); diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp new file mode 100644 index 0000000000..7b92a5bb3b --- /dev/null +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2023 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 "TestableScheduler.h" +#include "TestableSurfaceFlinger.h" + +namespace android::scheduler { + +TestableScheduler::TestableScheduler(RefreshRateSelectorPtr selectorPtr, + TestableSurfaceFlinger& testableSurfaceFlinger, + ISchedulerCallback& callback) + : TestableScheduler(std::make_unique<android::mock::VsyncController>(), + std::make_shared<android::mock::VSyncTracker>(), std::move(selectorPtr), + testableSurfaceFlinger.getFactory(), + testableSurfaceFlinger.getTimeStats(), callback) {} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index c0255d3e98..1e02c67d91 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -32,25 +32,25 @@ #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" +namespace android { +class TestableSurfaceFlinger; +} // namespace android + namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback) - : TestableScheduler(std::make_unique<mock::VsyncController>(), - std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr), - sp<VsyncModulator>::make(VsyncConfigSet{}), callback, - vsyncTrackerCallback) {} + TestableScheduler(RefreshRateSelectorPtr selectorPtr, + TestableSurfaceFlinger& testableSurfaceFlinger, ISchedulerCallback& callback); TestableScheduler(std::unique_ptr<VsyncController> controller, std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& schedulerCallback, - IVsyncTrackerCallback& vsyncTrackerCallback) + surfaceflinger::Factory& factory, TimeStats& timeStats, + ISchedulerCallback& schedulerCallback) : Scheduler(*this, schedulerCallback, (FeatureFlags)Feature::kContentDetection | Feature::kSmallDirtyContentDetection, - std::move(modulatorPtr), vsyncTrackerCallback) { + factory, selectorPtr->getActiveMode().fps, timeStats) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplay(displayId, std::move(selectorPtr), std::move(controller), std::move(tracker)); @@ -71,9 +71,14 @@ public: Scheduler::onFrameSignal(compositor, vsyncId, TimePoint()); } - // Used to inject mock event thread. - ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { - return Scheduler::createConnection(std::move(eventThread)); + void setEventThread(Cycle cycle, std::unique_ptr<EventThread> eventThreadPtr) { + if (cycle == Cycle::Render) { + mRenderEventThread = std::move(eventThreadPtr); + mRenderEventConnection = mRenderEventThread->createEventConnection(); + } else { + mLastCompositeEventThread = std::move(eventThreadPtr); + mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection(); + } } auto refreshRateSelector() { return pacesetterSelectorPtr(); } @@ -124,7 +129,6 @@ public: using Scheduler::resyncAllToHardwareVsync; - auto& mutableAppConnectionHandle() { return mAppConnectionHandle; } auto& mutableLayerHistory() { return mLayerHistory; } auto& mutableAttachedChoreographers() { return mAttachedChoreographers; } @@ -191,10 +195,6 @@ public: mPolicy.cachedModeChangedParams.reset(); } - void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { - Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); - } - void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) { auto schedule = getVsyncSchedule(id); std::lock_guard<std::mutex> lock(schedule->mHwVsyncLock); @@ -219,6 +219,7 @@ private: return {}; } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {} }; } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 22cb24bb6f..bce7729d80 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -53,7 +53,6 @@ #include "mock/MockFrameTimeline.h" #include "mock/MockFrameTracer.h" #include "mock/MockSchedulerCallback.h" -#include "mock/MockVsyncTrackerCallback.h" #include "mock/system/window/MockNativeWindow.h" #include "Scheduler/VSyncTracker.h" @@ -205,8 +204,6 @@ public: enum class SchedulerCallbackImpl { kNoOp, kMock }; - enum class VsyncTrackerCallbackImpl { kNoOp, kMock }; - struct DefaultDisplayMode { // The ID of the injected RefreshRateSelector and its default display mode. PhysicalDisplayId displayId; @@ -216,14 +213,17 @@ public: using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>; - void setupScheduler( - std::unique_ptr<scheduler::VsyncController> vsyncController, - std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, - std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, - DisplayModesVariant modesVariant, - SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, - VsyncTrackerCallbackImpl vsyncTrackerCallbackImpl = VsyncTrackerCallbackImpl::kNoOp, - bool useNiceMock = false) { + surfaceflinger::Factory& getFactory() { return mFactory; } + + TimeStats& getTimeStats() { return *mFlinger->mTimeStats; } + + void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, + std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, + std::unique_ptr<EventThread> appEventThread, + std::unique_ptr<EventThread> sfEventThread, + DisplayModesVariant modesVariant, + SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, + bool useNiceMock = false) { RefreshRateSelectorPtr selectorPtr = ftl::match( modesVariant, [](DefaultDisplayMode arg) { @@ -234,13 +234,6 @@ public: }, [](RefreshRateSelectorPtr selectorPtr) { return selectorPtr; }); - const auto fps = selectorPtr->getActiveMode().fps; - mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); - - mFlinger->mRefreshRateStats = - std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps, - hal::PowerMode::OFF); - mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); using ISchedulerCallback = scheduler::ISchedulerCallback; @@ -248,38 +241,26 @@ public: ? static_cast<ISchedulerCallback&>(mNoOpSchedulerCallback) : static_cast<ISchedulerCallback&>(mSchedulerCallback); - using VsyncTrackerCallback = scheduler::IVsyncTrackerCallback; - VsyncTrackerCallback& vsyncTrackerCallback = - vsyncTrackerCallbackImpl == VsyncTrackerCallbackImpl::kNoOp - ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback) - : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback); - - auto modulatorPtr = sp<scheduler::VsyncModulator>::make( - mFlinger->mVsyncConfiguration->getCurrentConfigs()); - if (useNiceMock) { mScheduler = new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController), std::move(vsyncTracker), std::move(selectorPtr), - std::move(modulatorPtr), - schedulerCallback, - vsyncTrackerCallback); + mFactory, + *mFlinger->mTimeStats, + schedulerCallback); } else { mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), - std::move(selectorPtr), - std::move(modulatorPtr), - schedulerCallback, vsyncTrackerCallback); + std::move(selectorPtr), mFactory, + *mFlinger->mTimeStats, schedulerCallback); } - mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms); + mScheduler->initVsync(*mTokenManager, 0ms); - mScheduler->mutableAppConnectionHandle() = - mScheduler->createConnection(std::move(appEventThread)); + mScheduler->setEventThread(scheduler::Cycle::Render, std::move(appEventThread)); + mScheduler->setEventThread(scheduler::Cycle::LastComposite, std::move(sfEventThread)); - mFlinger->mAppConnectionHandle = mScheduler->mutableAppConnectionHandle(); - mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); resetScheduler(mScheduler); } @@ -303,17 +284,16 @@ public: auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock); auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); EXPECT_CALL(*vsyncTracker, minFramePeriod()) .WillRepeatedly( Return(Period::fromNs(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD))); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), DefaultDisplayMode{options.displayId}, - SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp, - options.useNiceMock); + SchedulerCallbackImpl::kNoOp, options.useNiceMock); } void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } @@ -391,13 +371,14 @@ public: LOG_ALWAYS_FATAL_IF(!displayIdOpt); const auto displayId = *displayIdOpt; - constexpr bool kBackpressureGpuComposition = true; - scheduler::FrameTargeter frameTargeter(displayId, kBackpressureGpuComposition); + scheduler::FrameTargeter frameTargeter(displayId, + scheduler::Feature::kBackpressureGpuComposition); frameTargeter.beginFrame({.frameBeginTime = frameTime, .vsyncId = vsyncId, .expectedVsyncTime = expectedVsyncTime, - .sfWorkDuration = 10ms}, + .sfWorkDuration = 10ms, + .hwcMinWorkDuration = 10ms}, *mScheduler->getVsyncSchedule()); scheduler::FrameTargets targets; @@ -702,6 +683,36 @@ public: frameInterval, timeoutOpt); } + void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) { + ftl::FakeGuard guard(kMainThreadContext); + mFlinger->sendNotifyExpectedPresentHint(displayId); + } + + bool verifyHintIsScheduledOnPresent(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::ScheduleOnPresent; + } + + bool verifyHintIsSent(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::Sent; + } + + bool verifyHintStatusIsStart(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::Start; + } + + bool verifyHintStatusIsScheduledOnTx(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::ScheduleOnTx; + } + + bool verifyLastExpectedPresentTime(PhysicalDisplayId displayId, nsecs_t expectedPresentTime) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId) + .lastExpectedPresentTimestamp.ns() == expectedPresentTime; + } + void setNotifyExpectedPresentData(PhysicalDisplayId displayId, TimePoint lastExpectedPresentTimestamp, Fps lastFrameInterval) { @@ -710,6 +721,11 @@ public: displayData.lastFrameInterval = lastFrameInterval; } + void resetNotifyExpectedPresentHintState(PhysicalDisplayId displayId) { + mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus = + SurfaceFlinger::NotifyExpectedPresentHintStatus::Start; + } + ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the SurfaceFlinger instance may @@ -754,7 +770,6 @@ public: static constexpr int32_t DEFAULT_CONFIG_GROUP = 7; static constexpr int32_t DEFAULT_DPI = 320; static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0; - static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON; FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType, bool isPrimary) @@ -797,7 +812,7 @@ public: return *this; } - auto& setPowerMode(std::optional<hal::PowerMode> mode) { + auto& setPowerMode(hal::PowerMode mode) { mPowerMode = mode; return *this; } @@ -821,9 +836,7 @@ public: mHwcDisplayType); display->mutableIsConnected() = true; - if (mPowerMode) { - display->setPowerMode(*mPowerMode); - } + display->setPowerMode(mPowerMode); flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display); @@ -889,7 +902,7 @@ public: int32_t mDpiY = DEFAULT_DPI; int32_t mConfigGroup = DEFAULT_CONFIG_GROUP; hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG; - std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE; + hal::PowerMode mPowerMode = hal::PowerMode::ON; const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>* mCapabilities = nullptr; }; @@ -966,7 +979,7 @@ public: return *this; } - auto& setPowerMode(std::optional<hal::PowerMode> mode) { + auto& setPowerMode(hal::PowerMode mode) { mCreationArgs.initialPowerMode = mode; return *this; } @@ -1106,8 +1119,6 @@ private: sp<SurfaceFlinger> mFlinger; scheduler::mock::SchedulerCallback mSchedulerCallback; scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback; - scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; - scheduler::mock::NoOpVsyncTrackerCallback mNoOpVsyncTrackerCallback; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; scheduler::TestableScheduler* mScheduler = nullptr; Hwc2::mock::PowerAdvisor mPowerAdvisor; diff --git a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp index 4a83d445fc..d071ce985f 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp @@ -105,4 +105,16 @@ TEST_F(TransactionTraceWriterTest, overwriteOldFile) { verifyTraceFile(); } +// Check we cannot write to file if the trace write is disabled. +TEST_F(TransactionTraceWriterTest, canDisableTraceWriter) { + TransactionTraceWriter::getInstance().disable(); + TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true); + EXPECT_NE(access(mFilename.c_str(), F_OK), 0); + + TransactionTraceWriter::getInstance().enable(); + TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true); + EXPECT_EQ(access(mFilename.c_str(), F_OK), 0); + verifyTraceFile(); +} + } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 6a5635305a..c22deaba72 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -48,7 +48,7 @@ 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 onFrameBegin(TimePoint, TimePoint) final {} @@ -64,7 +64,7 @@ class FixedRateIdealStubTracker : public StubTracker { public: FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {} - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) 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) 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; @@ -117,7 +117,7 @@ public: mCallback.schedule( {.workDuration = mWorkload, .readyDuration = mReadyDuration, - .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration}); + .lastVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration}); for (auto i = 0u; i < iterations - 1; i++) { std::unique_lock lock(mMutex); @@ -130,7 +130,7 @@ public: mCallback.schedule({.workDuration = mWorkload, .readyDuration = mReadyDuration, - .earliestVsync = last + mWorkload + mReadyDuration}); + .lastVsync = last + mWorkload + mReadyDuration}); } // wait for the last callback. diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 2047018a15..9b70d92eac 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -46,14 +46,14 @@ using namespace com::android::graphics::surfaceflinger; class MockVSyncTracker : public mock::VSyncTracker { public: MockVSyncTracker(nsecs_t period) : mPeriod{period} { - ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_)) + ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_, _)) .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime)); ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); ON_CALL(*this, currentPeriod()) .WillByDefault(Invoke(this, &MockVSyncTracker::getCurrentPeriod)); } - nsecs_t nextVSyncTime(nsecs_t timePoint) const { + nsecs_t nextVSyncTime(nsecs_t timePoint, std::optional<nsecs_t>) const { if (timePoint % mPeriod == 0) { return timePoint; } @@ -243,12 +243,12 @@ TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) { mDispatchGroupThreshold, mVsyncMoveThreshold); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = 1000}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(900, *result); + EXPECT_EQ(900, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); } } @@ -257,12 +257,12 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = intended}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = intended}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(900, *result); + EXPECT_EQ(900, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); advanceToNextCallback(); @@ -277,18 +277,18 @@ TEST_F(VSyncDispatchTimerQueueTest, updateAlarmSettingFuture) { EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq); CountingCallback cb(mDispatch); - auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = intended}); + auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = intended}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(900, *result); + EXPECT_EQ(900, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); result = - mDispatch->update(cb, - {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); + mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(700, *result); + EXPECT_EQ(700, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); advanceToNextCallback(); @@ -303,17 +303,18 @@ TEST_F(VSyncDispatchTimerQueueTest, updateDoesntSchedule) { CountingCallback cb(mDispatch); const auto result = - mDispatch->update(cb, - {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); + mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended}); EXPECT_FALSE(result.has_value()); } TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150)); + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(mPeriod))) + .WillOnce(Return(1150)); EXPECT_CALL(mMockClock, alarmAt(_, 1050)); CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -324,7 +325,8 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) { auto const now = 234; mMockClock.advanceBy(234); auto const workDuration = 10 * mPeriod; - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + workDuration)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(now + workDuration, std::optional<nsecs_t>(mPeriod))) .WillOnce(Return(mPeriod * 11)); EXPECT_CALL(mMockClock, alarmAt(_, mPeriod)); @@ -332,9 +334,10 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) { const auto result = mDispatch->schedule(cb, {.workDuration = workDuration, .readyDuration = 0, - .earliestVsync = mPeriod}); + .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod, *result); + EXPECT_EQ(mPeriod, result->callbackTime.ns()); + EXPECT_EQ(workDuration + mPeriod, result->vsyncTime.ns()); } TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { @@ -342,12 +345,12 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod - 100, *result); + EXPECT_EQ(mPeriod - 100, result->callbackTime.ns()); + EXPECT_EQ(mPeriod, result->vsyncTime.ns()); EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled); } @@ -356,12 +359,12 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod - 100, *result); + EXPECT_EQ(mPeriod - 100, result->callbackTime.ns()); + EXPECT_EQ(mPeriod, result->vsyncTime.ns()); mMockClock.advanceBy(950); EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate); } @@ -371,12 +374,12 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) { EXPECT_CALL(mMockClock, alarmCancel()); PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s)); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod - 100, *result); + EXPECT_EQ(mPeriod - 100, result->callbackTime.ns()); + EXPECT_EQ(mPeriod, result->vsyncTime.ns()); std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); EXPECT_TRUE(cb.waitForPause()); @@ -393,12 +396,12 @@ TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { PausingCallback cb(mDispatch, 50ms); cb.stashResource(resource); - const auto result = mDispatch->schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod - 100, *result); + EXPECT_EQ(mPeriod - 100, result->callbackTime.ns()); + EXPECT_EQ(mPeriod, result->vsyncTime.ns()); std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); EXPECT_TRUE(cb.waitForPause()); @@ -413,7 +416,8 @@ TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { } TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000))) .Times(4) .WillOnce(Return(1055)) .WillOnce(Return(1063)) @@ -428,8 +432,8 @@ TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); - mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod}); + mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .lastVsync = mPeriod}); advanceToNextCallback(); advanceToNextCallback(); @@ -441,7 +445,7 @@ TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { } TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _)) .Times(4) .WillOnce(Return(1000)) .WillOnce(Return(2000)) @@ -455,21 +459,21 @@ TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 0}); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); EXPECT_THAT(cb.mCalls[0], Eq(1000)); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(2)); EXPECT_THAT(cb.mCalls[1], Eq(2000)); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); advanceToNextCallback(); @@ -478,7 +482,7 @@ TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { } TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _)) .Times(4) .WillOnce(Return(10000)) .WillOnce(Return(1000)) @@ -493,9 +497,8 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10}); - mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod * 10}); + mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .lastVsync = mPeriod}); mDispatch->cancel(cb1); } @@ -507,9 +510,9 @@ TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); } @@ -522,9 +525,9 @@ TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); } @@ -542,10 +545,9 @@ TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, - {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = closeOffset, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); ASSERT_THAT(cb0.mCalls.size(), Eq(1)); @@ -553,11 +555,9 @@ TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { ASSERT_THAT(cb1.mCalls.size(), Eq(1)); EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod)); - mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 2000}); mDispatch->schedule(cb1, - {.workDuration = notCloseOffset, - .readyDuration = 0, - .earliestVsync = 2000}); + {.workDuration = notCloseOffset, .readyDuration = 0, .lastVsync = 2000}); advanceToNextCallback(); ASSERT_THAT(cb1.mCalls.size(), Eq(2)); EXPECT_THAT(cb1.mCalls[1], Eq(2000)); @@ -577,32 +577,32 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); EXPECT_EQ(mDispatch->cancel(cb0), CancelResult::Cancelled); } TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _)) .Times(3) .WillOnce(Return(950)) .WillOnce(Return(1975)) .WillOnce(Return(2950)); CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 920}); mMockClock.advanceBy(850); EXPECT_THAT(cb.mCalls.size(), Eq(1)); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1900}); mMockClock.advanceBy(900); EXPECT_THAT(cb.mCalls.size(), Eq(1)); mMockClock.advanceBy(125); EXPECT_THAT(cb.mCalls.size(), Eq(2)); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2900}); mMockClock.advanceBy(975); EXPECT_THAT(cb.mCalls.size(), Eq(3)); } @@ -616,13 +616,11 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) { tmp = mDispatch->registerCallback( [&](auto, auto, auto) { mDispatch->schedule(tmp, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = 2000}); + {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); }, "o.o"); - mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); } @@ -631,30 +629,32 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) { std::optional<nsecs_t> lastTarget; tmp = mDispatch->registerCallback( [&](auto timestamp, auto, auto) { - auto result = - mDispatch->schedule(tmp, - {.workDuration = 400, - .readyDuration = 0, - .earliestVsync = timestamp - mVsyncMoveThreshold}); + auto result = mDispatch->schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .lastVsync = timestamp - mVsyncMoveThreshold}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod + timestamp - 400, *result); + EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns()); + EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns()); result = mDispatch->schedule(tmp, {.workDuration = 400, .readyDuration = 0, - .earliestVsync = timestamp}); + .lastVsync = timestamp}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod + timestamp - 400, *result); + EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns()); + EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns()); result = mDispatch->schedule(tmp, {.workDuration = 400, .readyDuration = 0, - .earliestVsync = timestamp + mVsyncMoveThreshold}); + .lastVsync = timestamp + mVsyncMoveThreshold}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod + timestamp - 400, *result); + EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns()); + EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns()); lastTarget = timestamp; }, "oo"); - mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); EXPECT_THAT(lastTarget, Eq(1000)); @@ -670,16 +670,16 @@ TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) { EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .lastVsync = 1000}); mMockClock.advanceBy(750); - mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); - mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .lastVsync = 2000}); mMockClock.advanceBy(800); - mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); } TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { @@ -692,12 +692,12 @@ TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); - mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000}); - mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .lastVsync = 2000}); + mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); advanceToNextCallback(); @@ -709,8 +709,8 @@ TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .lastVsync = 20000}); } TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { @@ -720,17 +720,15 @@ TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); CountingCallback cb0(mDispatch); - mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); mDispatch->cancel(cb0); - mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); } TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) { VSyncDispatch::CallbackToken token(100); EXPECT_FALSE( - mDispatch - ->schedule(token, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}) + mDispatch->schedule(token, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}) .has_value()); EXPECT_THAT(mDispatch->cancel(token), Eq(CancelResult::Error)); } @@ -738,33 +736,33 @@ TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) { TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) { CountingCallback cb0(mDispatch); auto result = - mDispatch->schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(500, *result); - result = mDispatch->schedule(cb0, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_EQ(500, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); + result = mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(900, *result); + EXPECT_EQ(900, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); } // b/1450138150 TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(500, *result); + EXPECT_EQ(500, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); mMockClock.advanceBy(400); - result = mDispatch->schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(1200, *result); + EXPECT_EQ(1200, result->callbackTime.ns()); + EXPECT_EQ(2000, result->vsyncTime.ns()); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -772,58 +770,60 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipASchedule // b/1450138150 TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTargetVSync) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); EXPECT_CALL(mMockClock, alarmAt(_, 400)).InSequence(seq); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(500, *result); + EXPECT_EQ(500, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); mMockClock.advanceBy(400); - result = mDispatch->schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(400, *result); + EXPECT_EQ(400, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); } TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000))) .Times(2) .WillOnce(Return(1000)) .WillOnce(Return(1002)); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(500, *result); + EXPECT_EQ(500, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); mMockClock.advanceBy(400); - result = mDispatch->schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(602, *result); + EXPECT_EQ(602, result->callbackTime.ns()); + EXPECT_EQ(1002, result->vsyncTime.ns()); } TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) { CountingCallback cb0(mDispatch); auto result = - mDispatch->schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(500, *result); + EXPECT_EQ(500, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); advanceToNextCallback(); - result = mDispatch->schedule(cb0, - {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}); + result = + mDispatch->schedule(cb0, {.workDuration = 1100, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(900, *result); + EXPECT_EQ(900, result->callbackTime.ns()); + EXPECT_EQ(2000, result->vsyncTime.ns()); } TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { @@ -832,39 +832,40 @@ TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq); CountingCallback cb0(mDispatch); auto result = - mDispatch->schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(500, *result); + EXPECT_EQ(500, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); advanceToNextCallback(); - result = mDispatch->schedule(cb0, - {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}); + result = + mDispatch->schedule(cb0, {.workDuration = 1900, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(1100, *result); + EXPECT_EQ(1100, result->callbackTime.ns()); + EXPECT_EQ(3000, result->vsyncTime.ns()); } TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); EXPECT_CALL(mMockClock, alarmAt(_, 600)); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(600, *result); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); - result = mDispatch->schedule(cb, - {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(600, *result); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(2000, result->vsyncTime.ns()); advanceToNextCallback(); } TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); @@ -872,15 +873,15 @@ TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) { CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(600, *result); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); - result = mDispatch->schedule(cb, - {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(0, *result); + EXPECT_EQ(0, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); advanceToNextCallback(); } @@ -892,10 +893,10 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMove) { VSyncCallbackRegistration cb( mDispatch, [](auto, auto, auto) {}, ""); VSyncCallbackRegistration cb1(std::move(cb)); - cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + cb.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); cb.cancel(); - cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + cb1.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); cb1.cancel(); } @@ -908,10 +909,10 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) { VSyncCallbackRegistration cb1( mDispatch, [](auto, auto, auto) {}, ""); cb1 = std::move(cb); - cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + cb.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 1000}); cb.cancel(); - cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + cb1.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); cb1.cancel(); } @@ -924,18 +925,18 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent CountingCallback cb2(mDispatch); auto result = - mDispatch->schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(600, *result); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); mMockClock.setLag(100); mMockClock.advanceBy(620); - result = mDispatch->schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(1900, *result); + EXPECT_EQ(1900, result->callbackTime.ns()); + EXPECT_EQ(2000, result->vsyncTime.ns()); mMockClock.advanceBy(80); EXPECT_THAT(cb1.mCalls.size(), Eq(1)); @@ -946,30 +947,65 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent // If the same callback tries to reschedule itself after it's too late, timer opts to apply the // update later, as opposed to blocking the calling thread. TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) { + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); + Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(600, *result); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); mMockClock.setLag(100); mMockClock.advanceBy(620); - result = mDispatch->schedule(cb, - {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(1630, *result); + EXPECT_EQ(1630, result->callbackTime.ns()); + EXPECT_EQ(2000, result->vsyncTime.ns()); mMockClock.advanceBy(80); EXPECT_THAT(cb.mCalls.size(), Eq(1)); } // b/154303580. +// If the same callback tries to reschedule itself after it's too late, timer opts to apply the +// update later, as opposed to blocking the calling thread. +TEST_F(VSyncDispatchTimerQueueTest, doesntSkipSchedulingIfTimerReschedulingIsImminentSameCallback) { + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); + + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq); + CountingCallback cb(mDispatch); + + auto result = + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); + + mMockClock.setLag(100); + mMockClock.advanceBy(620); + + result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); + mMockClock.advanceBy(80); + + ASSERT_EQ(1, cb.mCalls.size()); + EXPECT_EQ(1000, cb.mCalls[0]); + + ASSERT_EQ(1, cb.mWakeupTime.size()); + EXPECT_EQ(600, cb.mWakeupTime[0]); +} + +// b/154303580. TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) { Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); @@ -978,14 +1014,14 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) { CountingCallback cb2(mDispatch); auto result = - mDispatch->schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(600, *result); - result = mDispatch->schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); + result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(1900, *result); + EXPECT_EQ(1900, result->callbackTime.ns()); + EXPECT_EQ(2000, result->vsyncTime.ns()); mMockClock.setLag(100); mMockClock.advanceBy(620); @@ -1007,14 +1043,14 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) { CountingCallback cb2(mDispatch); auto result = - mDispatch->schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(600, *result); - result = mDispatch->schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); + result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(1900, *result); + EXPECT_EQ(1900, result->callbackTime.ns()); + EXPECT_EQ(2000, result->vsyncTime.ns()); mMockClock.setLag(100); mMockClock.advanceBy(620); @@ -1034,23 +1070,25 @@ TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) { CountingCallback cb2(mDispatch); Sequence seq; - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000))) .InSequence(seq) .WillOnce(Return(1000)); EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000))) .InSequence(seq) .WillOnce(Return(1000)); auto result = - mDispatch->schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(600, *result); - result = mDispatch->schedule(cb2, - {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_EQ(600, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); + result = mDispatch->schedule(cb2, {.workDuration = 390, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(610, *result); + EXPECT_EQ(610, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); mMockClock.setLag(100); mMockClock.advanceBy(700); @@ -1070,12 +1108,12 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - const auto result = mDispatch->schedule(cb, - {.workDuration = 70, - .readyDuration = 30, - .earliestVsync = intended}); + const auto result = + mDispatch->schedule(cb, + {.workDuration = 70, .readyDuration = 30, .lastVsync = intended}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(900, *result); + EXPECT_EQ(900, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -1087,15 +1125,15 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) { } TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); @@ -1110,7 +1148,7 @@ TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) { } TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); @@ -1118,8 +1156,8 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { CountingCallback cb(mDispatch); - mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000}); advanceToNextCallback(); @@ -1134,44 +1172,44 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { } TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(500, *result); + EXPECT_EQ(500, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); mMockClock.advanceBy(300); - result = mDispatch->schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(1200, *result); + EXPECT_EQ(1200, result->callbackTime.ns()); + EXPECT_EQ(2000, result->vsyncTime.ns()); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); } TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); EXPECT_CALL(mMockClock, alarmAt(_, 300)).InSequence(seq); CountingCallback cb(mDispatch); auto result = - mDispatch->schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(500, *result); + EXPECT_EQ(500, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); mMockClock.advanceBy(300); - result = mDispatch->schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000}); EXPECT_TRUE(result.has_value()); - EXPECT_EQ(300, *result); + EXPECT_EQ(300, result->callbackTime.ns()); + EXPECT_EQ(1000, result->vsyncTime.ns()); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -1204,9 +1242,11 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) { "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + const auto scheduleResult = + entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0); + EXPECT_EQ(900, scheduleResult.callbackTime.ns()); + EXPECT_EQ(1000, scheduleResult.vsyncTime.ns()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(900)); @@ -1219,16 +1259,19 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) auto const duration = 500; auto const now = 8750; - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + duration)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(now + duration, std::optional<nsecs_t>(994))) .Times(1) .WillOnce(Return(10000)); VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994}, - *mStubTracker.get(), now) - .has_value()); + const auto scheduleResult = + entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994}, + *mStubTracker, now); + EXPECT_EQ(9500, scheduleResult.callbackTime.ns()); + EXPECT_EQ(10000, scheduleResult.vsyncTime.ns()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(9500)); @@ -1249,9 +1292,11 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { }, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + const auto scheduleResult = + entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0); + EXPECT_EQ(900, scheduleResult.callbackTime.ns()); + EXPECT_EQ(1000, scheduleResult.vsyncTime.ns()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(900)); @@ -1272,7 +1317,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { } TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _)) .Times(2) .WillOnce(Return(1000)) .WillOnce(Return(1020)); @@ -1281,17 +1326,19 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - entry.update(*mStubTracker.get(), 0); + entry.update(*mStubTracker, 0); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + const auto scheduleResult = + entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0); + EXPECT_EQ(900, scheduleResult.callbackTime.ns()); + EXPECT_EQ(1000, scheduleResult.vsyncTime.ns()); auto wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(wakeup, Eq(900)); - entry.update(*mStubTracker.get(), 0); + entry.update(*mStubTracker, 0); wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(920)); @@ -1300,9 +1347,10 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + EXPECT_EQ(900, + entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker.get(), 0) + .callbackTime.ns()); entry.update(*mStubTracker.get(), 0); auto const wakeup = entry.wakeupTime(); @@ -1313,26 +1361,32 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + auto scheduleResult = + entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0); + + EXPECT_EQ(900, scheduleResult.callbackTime.ns()); + EXPECT_EQ(1000, scheduleResult.vsyncTime.ns()); entry.executing(); // 1000 is executing // had 1000 not been executing, this could have been scheduled for time 800. - EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0); + EXPECT_EQ(1800, scheduleResult.callbackTime.ns()); + EXPECT_EQ(2000, scheduleResult.vsyncTime.ns()); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); - EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + scheduleResult = entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0); + EXPECT_EQ(1950, scheduleResult.callbackTime.ns()); + EXPECT_EQ(2000, scheduleResult.vsyncTime.ns()); EXPECT_THAT(*entry.wakeupTime(), Eq(1950)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); - EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001}, - *mStubTracker.get(), 0) - .has_value()); + scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001}, + *mStubTracker, 0); + EXPECT_EQ(1800, scheduleResult.callbackTime.ns()); + EXPECT_EQ(2000, scheduleResult.vsyncTime.ns()); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); } @@ -1343,54 +1397,75 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); Sequence seq; - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500, std::optional<nsecs_t>(500))) .InSequence(seq) .WillOnce(Return(1000)); - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500, std::optional<nsecs_t>(500))) .InSequence(seq) .WillOnce(Return(1000)); - EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold)) + EXPECT_CALL(*mStubTracker.get(), + nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold, + std::optional<nsecs_t>(1000))) .InSequence(seq) .WillOnce(Return(2000)); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + auto scheduleResult = + entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0); + EXPECT_EQ(900, scheduleResult.callbackTime.ns()); + EXPECT_EQ(1000, scheduleResult.vsyncTime.ns()); entry.executing(); // 1000 is executing - EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0); + EXPECT_EQ(1800, scheduleResult.callbackTime.ns()); + EXPECT_EQ(2000, scheduleResult.vsyncTime.ns()); } TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) { - VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); - EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); - EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); - EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + VSyncDispatchTimerQueueEntry entry("test", [](auto, auto, auto) {}, mVsyncMoveThreshold); + EXPECT_EQ(900, + entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0) + .callbackTime.ns()); + EXPECT_EQ(800, + entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0) + .callbackTime.ns()); + EXPECT_EQ(950, + entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0) + .callbackTime.ns()); + { + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); + EXPECT_EQ(0, + entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0) + .callbackTime.ns()); + } + { + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); + EXPECT_EQ(800, + entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500}, + *mStubTracker, 0) + .callbackTime.ns()); + } } -TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) { +TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdateAndDontSkip) { static constexpr auto effectualOffset = 200; VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); - entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400}); - entry.addPendingWorkloadUpdate( - {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400}); + entry.addPendingWorkloadUpdate(*mStubTracker.get(), 0, + {.workDuration = 100, .readyDuration = 0, .lastVsync = 400}); + entry.addPendingWorkloadUpdate(*mStubTracker.get(), 0, + {.workDuration = effectualOffset, + .readyDuration = 0, + .lastVsync = 400}); EXPECT_TRUE(entry.hasPendingWorkloadUpdate()); - entry.update(*mStubTracker.get(), 0); + entry.update(*mStubTracker, 0); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset)); } @@ -1410,9 +1485,11 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) { }, mVsyncMoveThreshold); - EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500}, - *mStubTracker.get(), 0) - .has_value()); + const auto scheduleResult = + entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500}, + *mStubTracker, 0); + EXPECT_EQ(900, scheduleResult.callbackTime.ns()); + EXPECT_EQ(mPeriod, scheduleResult.vsyncTime.ns()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(900)); diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 7a498c9d29..6b9ea56062 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -26,12 +26,12 @@ #include <common/test/FlagUtils.h> #include "Scheduler/VSyncPredictor.h" #include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/MockVsyncTrackerCallback.h" #include <gmock/gmock.h> #include <gtest/gtest.h> #include <algorithm> #include <chrono> +#include <optional> #include <utility> #include <com_android_graphics_surfaceflinger_flags.h> @@ -75,20 +75,43 @@ 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 { nsecs_t mNow = 0; nsecs_t mPeriod = 1000; ftl::NonNull<DisplayModePtr> mMode = displayMode(mPeriod); - scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; static constexpr size_t kHistorySize = 10; static constexpr size_t kMinimumSamplesForPrediction = 6; static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; - VSyncPredictor tracker{mMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent, mVsyncTrackerCallback}; + std::shared_ptr<TestClock> mClock{std::make_shared<TestClock>()}; + + VSyncPredictor tracker{std::make_unique<ClockWrapper>(mClock), mMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { @@ -409,8 +432,8 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { const auto mode = displayMode(mPeriod); - VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent, - mVsyncTrackerCallback}; + 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, @@ -608,35 +631,6 @@ TEST_F(VSyncPredictorTest, setRenderRateIsRespected) { 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(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)); -} - TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { auto last = mNow; for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { @@ -657,48 +651,6 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } -TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { - SET_FLAG_FOR_TEST(flags::vrr_config, true); - - const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - NotifyExpectedPresentConfig notifyExpectedPresentConfig; - notifyExpectedPresentConfig.notifyExpectedPresentTimeoutNs = Period::fromNs(30).ns(); - - hal::VrrConfig vrrConfig; - vrrConfig.notifyExpectedPresentConfig = notifyExpectedPresentConfig; - vrrConfig.minFrameIntervalNs = refreshRate.getPeriodNsecs(); - - const int32_t kGroup = 0; - const auto kResolution = ui::Size(1920, 1080); - const auto mode = - ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig, kGroup, - kResolution, DEFAULT_DISPLAY_ID)); - - tracker.setDisplayModePtr(mode); - auto last = mNow; - for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { - EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(TimePoint::fromNs(last + mPeriod), mode, - FpsMatcher(refreshRate))) - .Times(1); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); - mNow += mPeriod; - last = mNow; - tracker.addVsyncTimestamp(mNow); - } - - tracker.setRenderRate(refreshRate / 2); - { - // out of render rate phase - EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(TimePoint::fromNs(mNow + 3 * mPeriod), mode, - FpsMatcher(refreshRate / 2))) - .Times(1); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), - Eq(mNow + 3 * mPeriod)); - } -} - TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { SET_FLAG_FOR_TEST(flags::vrr_config, true); @@ -714,21 +666,59 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { .setVrrConfig(std::move(vrrConfig)) .build()); - VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent, mVsyncTrackerCallback}; + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; vrrTracker.setRenderRate(minFrameRate); vrrTracker.addVsyncTimestamp(0); EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); - EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1300)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000)); vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500)); - EXPECT_EQ(1500, vrrTracker.nextAnticipatedVSyncTimeFrom(1300)); - EXPECT_EQ(2500, vrrTracker.nextAnticipatedVSyncTimeFrom(2300)); - - vrrTracker.onFrameMissed(TimePoint::fromNs(2500)); - EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2300)); - EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3300)); + EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 2000)); + EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(3500, 3500)); + + // Miss when starting 4500 and expect the next vsync will be at 5000 (next one) + vrrTracker.onFrameBegin(TimePoint::fromNs(3500), TimePoint::fromNs(2500)); + 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, 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)); + 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(8000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(10000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(10000)); + + tracker.setRenderRate(Fps::fromPeriodNsecs(3000)); + 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(8000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(10000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(10000)); + 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 diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h index 5bcce501e6..685d8f9e95 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h @@ -24,7 +24,7 @@ inline DisplayMode::Builder createDisplayModeBuilder( DisplayModeId modeId, Fps displayRefreshRate, int32_t group = 0, ui::Size resolution = ui::Size(1920, 1080), PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0)) { - return DisplayMode::Builder(hal::HWConfigId(modeId.value())) + return DisplayMode::Builder(hal::HWConfigId(ftl::to_underlying(modeId))) .setId(modeId) .setPhysicalDisplayId(displayId) .setVsyncPeriod(displayRefreshRate.getPeriodNsecs()) @@ -45,8 +45,8 @@ inline DisplayModePtr createDisplayMode(PhysicalDisplayId displayId, DisplayMode } inline DisplayModePtr createVrrDisplayMode( - DisplayModeId modeId, Fps displayRefreshRate, hal::VrrConfig vrrConfig, int32_t group = 0, - ui::Size resolution = ui::Size(1920, 1080), + DisplayModeId modeId, Fps displayRefreshRate, std::optional<hal::VrrConfig> vrrConfig, + int32_t group = 0, ui::Size resolution = ui::Size(1920, 1080), PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0)) { return createDisplayModeBuilder(modeId, displayRefreshRate, group, resolution, displayId) .setVrrConfig(std::move(vrrConfig)) diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 7413235c19..602bdfc152 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -53,7 +53,8 @@ public: MOCK_METHOD(hal::Error, getRequests, (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)), (override)); - MOCK_METHOD(hal::Error, getConnectionType, (ui::DisplayConnectionType *), (const, override)); + MOCK_METHOD((ftl::Expected<ui::DisplayConnectionType, hal::Error>), getConnectionType, (), + (const, override)); MOCK_METHOD(hal::Error, supportsDoze, (bool *), (const, override)); MOCK_METHOD(hal::Error, getHdrCapabilities, (android::HdrCapabilities *), (const, override)); MOCK_METHOD(hal::Error, getDisplayedContentSamplingAttributes, diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h index a088aabc11..ed1405b058 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h @@ -18,12 +18,21 @@ #include "binder/Status.h" +// FMQ library in IPower does questionable conversions +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" #include <aidl/android/hardware/power/IPower.h> +#pragma clang diagnostic pop + #include <gmock/gmock.h> using aidl::android::hardware::power::Boost; +using aidl::android::hardware::power::ChannelConfig; using aidl::android::hardware::power::IPower; using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::SessionConfig; +using aidl::android::hardware::power::SessionTag; + using aidl::android::hardware::power::Mode; using android::binder::Status; @@ -42,6 +51,14 @@ public: int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session), (override)); MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); + MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig, + (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, SessionTag tag, SessionConfig* config, + std::shared_ptr<IPowerHintSession>* _aidl_return), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel, + (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override)); + MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h index 364618d61a..27564b26de 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h @@ -18,10 +18,15 @@ #include "binder/Status.h" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" #include <aidl/android/hardware/power/IPower.h> +#pragma clang diagnostic pop + #include <gmock/gmock.h> using aidl::android::hardware::power::IPowerHintSession; +using aidl::android::hardware::power::SessionConfig; using aidl::android::hardware::power::SessionHint; using aidl::android::hardware::power::SessionMode; using android::binder::Status; @@ -47,6 +52,7 @@ public: MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override)); MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override)); MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSessionConfig, (SessionConfig * _aidl_return), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h index 68fe3c52d4..ae41e7ea75 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h @@ -19,7 +19,10 @@ #include <gmock/gmock.h> #include <scheduler/Time.h> +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" #include <powermanager/PowerHalController.h> +#pragma clang diagnostic pop namespace android { namespace hardware { @@ -31,8 +34,6 @@ class IPower; namespace android::Hwc2::mock { -using aidl::android::hardware::power::Boost; -using aidl::android::hardware::power::Mode; using android::power::HalResult; class MockPowerHalController : public power::PowerHalController { @@ -40,12 +41,22 @@ public: MockPowerHalController(); ~MockPowerHalController() override; MOCK_METHOD(void, init, (), (override)); - MOCK_METHOD(HalResult<void>, setBoost, (Boost, int32_t), (override)); - MOCK_METHOD(HalResult<void>, setMode, (Mode, bool), (override)); + MOCK_METHOD(HalResult<void>, setBoost, (aidl::android::hardware::power::Boost, int32_t), + (override)); + MOCK_METHOD(HalResult<void>, setMode, (aidl::android::hardware::power::Mode, bool), (override)); MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>, createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t), (override)); + MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>, + createHintSessionWithConfig, + (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config), + (override)); MOCK_METHOD(HalResult<int64_t>, getHintSessionPreferredRate, (), (override)); + MOCK_METHOD(HalResult<aidl::android::hardware::power::ChannelConfig>, getSessionChannel, + (int tgid, int uid), (override)); + MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override)); }; } // namespace android::Hwc2::mock
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h index a71e82cc75..7b18a82283 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h +++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h @@ -18,12 +18,15 @@ #include <android/gui/DisplayModeSpecs.h> +#include "DisplayHardware/DisplayMode.h" + namespace android::mock { -inline gui::DisplayModeSpecs createDisplayModeSpecs(int32_t defaultMode, bool allowGroupSwitching, - float minFps, float maxFps) { +inline gui::DisplayModeSpecs createDisplayModeSpecs(DisplayModeId defaultMode, + bool allowGroupSwitching, float minFps, + float maxFps) { gui::DisplayModeSpecs specs; - specs.defaultMode = defaultMode; + specs.defaultMode = ftl::to_underlying(defaultMode); specs.allowGroupSwitching = allowGroupSwitching; specs.primaryRanges.physical.min = minFps; specs.primaryRanges.physical.max = maxFps; diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 866af3bbd0..e2b0ed1df9 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -59,6 +59,9 @@ public: MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&)); MOCK_METHOD(void, pauseVsyncCallback, (bool)); MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override)); + MOCK_METHOD(void, onHdcpLevelsChanged, + (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel), + (override)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h index 22b2cccf03..4ca05423d7 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -28,6 +28,8 @@ struct SchedulerCallback final : ISchedulerCallback { MOCK_METHOD(void, kernelTimerChanged, (bool), (override)); MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override)); MOCK_METHOD(void, onChoreographerAttached, (), (override)); + MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), + (override)); }; struct NoOpSchedulerCallback final : ISchedulerCallback { @@ -36,6 +38,7 @@ struct NoOpSchedulerCallback final : ISchedulerCallback { void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} void onChoreographerAttached() override {} + void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} }; } // namespace android::scheduler::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h index dc32ff969c..1dc2ef42d6 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h @@ -29,8 +29,10 @@ public: MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override)); MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override)); - MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override)); - MOCK_METHOD(scheduler::ScheduleResult, update, (CallbackToken, ScheduleTiming), (override)); + MOCK_METHOD(std::optional<scheduler::ScheduleResult>, schedule, (CallbackToken, ScheduleTiming), + (override)); + MOCK_METHOD(std::optional<scheduler::ScheduleResult>, update, (CallbackToken, ScheduleTiming), + (override)); MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override)); MOCK_METHOD(void, dump, (std::string&), (const, override)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index e588bb9a3f..6d10a5cc5d 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -28,12 +28,13 @@ public: ~VSyncTracker() override; MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override)); - MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t), (const, override)); + MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t, std::optional<nsecs_t>), + (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, onFrameBegin, (TimePoint, TimePoint), (override)); diff --git a/services/surfaceflinger/tests/vsync/Android.bp b/services/surfaceflinger/tests/vsync/Android.bp index bae9796598..c7daa881ae 100644 --- a/services/surfaceflinger/tests/vsync/Android.bp +++ b/services/surfaceflinger/tests/vsync/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_binary { @@ -33,6 +34,6 @@ cc_binary { "libgui", "libui", "libutils", - ] + ], } diff --git a/services/surfaceflinger/tests/waitforvsync/Android.bp b/services/surfaceflinger/tests/waitforvsync/Android.bp index ffed4d77c7..734fc20343 100644 --- a/services/surfaceflinger/tests/waitforvsync/Android.bp +++ b/services/surfaceflinger/tests/waitforvsync/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_binary { @@ -31,5 +32,5 @@ cc_binary { ], shared_libs: [ "libcutils", - ] + ], } diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp index 63ecaec06f..f10ba44d74 100644 --- a/services/vibratorservice/VibratorHalWrapper.cpp +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -56,75 +56,6 @@ bool isStaticCastValid(Effect effect) { // ------------------------------------------------------------------------------------------------- -const constexpr char* STATUS_T_ERROR_MESSAGE_PREFIX = "status_t = "; -const constexpr char* STATUS_V_1_0_ERROR_MESSAGE_PREFIX = - "android::hardware::vibrator::V1_0::Status = "; - -template <typename T> -HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) { - switch (status) { - case V1_0::Status::OK: - return HalResult<T>::ok(data); - case V1_0::Status::UNSUPPORTED_OPERATION: - return HalResult<T>::unsupported(); - default: - return HalResult<T>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status)); - } -} - -template <typename T> -template <typename R> -HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) { - return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description()); -} - -template <typename T> -template <typename R> -HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) { - return ret.isOk() ? HalResult<T>::fromStatus(status, data) - : HalResult<T>::failed(ret.description()); -} - -// ------------------------------------------------------------------------------------------------- - -HalResult<void> HalResult<void>::fromStatus(status_t status) { - if (status == android::OK) { - return HalResult<void>::ok(); - } - return HalResult<void>::failed(STATUS_T_ERROR_MESSAGE_PREFIX + statusToString(status)); -} - -HalResult<void> HalResult<void>::fromStatus(binder::Status status) { - if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION || - status.transactionError() == android::UNKNOWN_TRANSACTION) { - // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is - // the same as the operation being unsupported by this HAL. Should not retry. - return HalResult<void>::unsupported(); - } - if (status.isOk()) { - return HalResult<void>::ok(); - } - return HalResult<void>::failed(std::string(status.toString8().c_str())); -} - -HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) { - switch (status) { - case V1_0::Status::OK: - return HalResult<void>::ok(); - case V1_0::Status::UNSUPPORTED_OPERATION: - return HalResult<void>::unsupported(); - default: - return HalResult<void>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status)); - } -} - -template <typename R> -HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) { - return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description()); -} - -// ------------------------------------------------------------------------------------------------- - Info HalWrapper::getInfo() { getCapabilities(); getPrimitiveDurations(); @@ -269,7 +200,7 @@ HalResult<std::vector<float>> HalWrapper::getMaxAmplitudesInternal() { // ------------------------------------------------------------------------------------------------- HalResult<void> AidlHalWrapper::ping() { - return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder()); + return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder()); } void AidlHalWrapper::tryReconnect() { @@ -291,7 +222,7 @@ HalResult<void> AidlHalWrapper::on(milliseconds timeout, static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK); auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; - auto ret = HalResult<void>::fromStatus(getHal()->on(timeout.count(), cb)); + auto ret = HalResultFactory::fromStatus(getHal()->on(timeout.count(), cb)); if (!supportsCallback && ret.isOk()) { mCallbackScheduler->schedule(completionCallback, timeout); } @@ -300,23 +231,23 @@ HalResult<void> AidlHalWrapper::on(milliseconds timeout, } HalResult<void> AidlHalWrapper::off() { - return HalResult<void>::fromStatus(getHal()->off()); + return HalResultFactory::fromStatus(getHal()->off()); } HalResult<void> AidlHalWrapper::setAmplitude(float amplitude) { - return HalResult<void>::fromStatus(getHal()->setAmplitude(amplitude)); + return HalResultFactory::fromStatus(getHal()->setAmplitude(amplitude)); } HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) { - return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled)); + return HalResultFactory::fromStatus(getHal()->setExternalControl(enabled)); } HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) { - return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength)); + return HalResultFactory::fromStatus(getHal()->alwaysOnEnable(id, effect, strength)); } HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) { - return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id)); + return HalResultFactory::fromStatus(getHal()->alwaysOnDisable(id)); } HalResult<milliseconds> AidlHalWrapper::performEffect( @@ -330,7 +261,7 @@ HalResult<milliseconds> AidlHalWrapper::performEffect( auto result = getHal()->perform(effect, strength, cb, &lengthMs); milliseconds length = milliseconds(lengthMs); - auto ret = HalResult<milliseconds>::fromStatus(result, length); + auto ret = HalResultFactory::fromStatus<milliseconds>(result, length); if (!supportsCallback && ret.isOk()) { mCallbackScheduler->schedule(completionCallback, length); } @@ -357,38 +288,40 @@ HalResult<milliseconds> AidlHalWrapper::performComposedEffect( duration += milliseconds(effect.delayMs); } - return HalResult<milliseconds>::fromStatus(getHal()->compose(primitives, cb), duration); + return HalResultFactory::fromStatus<milliseconds>(getHal()->compose(primitives, cb), duration); } HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwle>& primitives, const std::function<void()>& completionCallback) { // This method should always support callbacks, so no need to double check. auto cb = new HalCallbackWrapper(completionCallback); - return HalResult<void>::fromStatus(getHal()->composePwle(primitives, cb)); + return HalResultFactory::fromStatus(getHal()->composePwle(primitives, cb)); } HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() { int32_t capabilities = 0; auto result = getHal()->getCapabilities(&capabilities); - return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities)); + return HalResultFactory::fromStatus<Capabilities>(result, + static_cast<Capabilities>(capabilities)); } HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() { std::vector<Effect> supportedEffects; auto result = getHal()->getSupportedEffects(&supportedEffects); - return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects); + return HalResultFactory::fromStatus<std::vector<Effect>>(result, supportedEffects); } HalResult<std::vector<Braking>> AidlHalWrapper::getSupportedBrakingInternal() { std::vector<Braking> supportedBraking; auto result = getHal()->getSupportedBraking(&supportedBraking); - return HalResult<std::vector<Braking>>::fromStatus(result, supportedBraking); + return HalResultFactory::fromStatus<std::vector<Braking>>(result, supportedBraking); } HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() { std::vector<CompositePrimitive> supportedPrimitives; auto result = getHal()->getSupportedPrimitives(&supportedPrimitives); - return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives); + return HalResultFactory::fromStatus<std::vector<CompositePrimitive>>(result, + supportedPrimitives); } HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsInternal( @@ -408,7 +341,7 @@ HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsIntern } int32_t duration = 0; auto result = getHal()->getPrimitiveDuration(primitive, &duration); - auto halResult = HalResult<int32_t>::fromStatus(result, duration); + auto halResult = HalResultFactory::fromStatus<int32_t>(result, duration); if (halResult.isUnsupported()) { // Should not happen, supported primitives should always support requesting duration. ALOGE("Supported primitive %zu returned unsupported for getPrimitiveDuration", @@ -427,55 +360,55 @@ HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsIntern HalResult<milliseconds> AidlHalWrapper::getPrimitiveDelayMaxInternal() { int32_t delay = 0; auto result = getHal()->getCompositionDelayMax(&delay); - return HalResult<milliseconds>::fromStatus(result, milliseconds(delay)); + return HalResultFactory::fromStatus<milliseconds>(result, milliseconds(delay)); } HalResult<milliseconds> AidlHalWrapper::getPrimitiveDurationMaxInternal() { int32_t delay = 0; auto result = getHal()->getPwlePrimitiveDurationMax(&delay); - return HalResult<milliseconds>::fromStatus(result, milliseconds(delay)); + return HalResultFactory::fromStatus<milliseconds>(result, milliseconds(delay)); } HalResult<int32_t> AidlHalWrapper::getCompositionSizeMaxInternal() { int32_t size = 0; auto result = getHal()->getCompositionSizeMax(&size); - return HalResult<int32_t>::fromStatus(result, size); + return HalResultFactory::fromStatus<int32_t>(result, size); } HalResult<int32_t> AidlHalWrapper::getPwleSizeMaxInternal() { int32_t size = 0; auto result = getHal()->getPwleCompositionSizeMax(&size); - return HalResult<int32_t>::fromStatus(result, size); + return HalResultFactory::fromStatus<int32_t>(result, size); } HalResult<float> AidlHalWrapper::getMinFrequencyInternal() { float minFrequency = 0; auto result = getHal()->getFrequencyMinimum(&minFrequency); - return HalResult<float>::fromStatus(result, minFrequency); + return HalResultFactory::fromStatus<float>(result, minFrequency); } HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() { float f0 = 0; auto result = getHal()->getResonantFrequency(&f0); - return HalResult<float>::fromStatus(result, f0); + return HalResultFactory::fromStatus<float>(result, f0); } HalResult<float> AidlHalWrapper::getFrequencyResolutionInternal() { float frequencyResolution = 0; auto result = getHal()->getFrequencyResolution(&frequencyResolution); - return HalResult<float>::fromStatus(result, frequencyResolution); + return HalResultFactory::fromStatus<float>(result, frequencyResolution); } HalResult<float> AidlHalWrapper::getQFactorInternal() { float qFactor = 0; auto result = getHal()->getQFactor(&qFactor); - return HalResult<float>::fromStatus(result, qFactor); + return HalResultFactory::fromStatus<float>(result, qFactor); } HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() { std::vector<float> amplitudes; auto result = getHal()->getBandwidthAmplitudeMap(&litudes); - return HalResult<std::vector<float>>::fromStatus(result, amplitudes); + return HalResultFactory::fromStatus<std::vector<float>>(result, amplitudes); } sp<Aidl::IVibrator> AidlHalWrapper::getHal() { @@ -488,7 +421,7 @@ sp<Aidl::IVibrator> AidlHalWrapper::getHal() { template <typename I> HalResult<void> HidlHalWrapper<I>::ping() { auto result = getHal()->ping(); - return HalResult<void>::fromReturn(result); + return HalResultFactory::fromReturn(result); } template <typename I> @@ -504,7 +437,7 @@ template <typename I> HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout, const std::function<void()>& completionCallback) { auto result = getHal()->on(timeout.count()); - auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + auto ret = HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); if (ret.isOk()) { mCallbackScheduler->schedule(completionCallback, timeout); } @@ -514,14 +447,14 @@ HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout, template <typename I> HalResult<void> HidlHalWrapper<I>::off() { auto result = getHal()->off(); - return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); } template <typename I> HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) { uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max()); auto result = getHal()->setAmplitude(amp); - return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); } template <typename I> @@ -547,7 +480,7 @@ HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() { hardware::Return<bool> result = getHal()->supportsAmplitudeControl(); Capabilities capabilities = result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE; - return HalResult<Capabilities>::fromReturn(result, capabilities); + return HalResultFactory::fromReturn<Capabilities>(result, capabilities); } template <typename I> @@ -566,7 +499,7 @@ HalResult<milliseconds> HidlHalWrapper<I>::performInternal( auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback); milliseconds length = milliseconds(lengthMs); - auto ret = HalResult<milliseconds>::fromReturn(result, status, length); + auto ret = HalResultFactory::fromReturn<milliseconds>(result, status, length); if (ret.isOk()) { mCallbackScheduler->schedule(completionCallback, length); } @@ -638,7 +571,7 @@ HalResult<milliseconds> HidlHalWrapperV1_2::performEffect( HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) { auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled)); - return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); } HalResult<milliseconds> HidlHalWrapperV1_3::performEffect( @@ -671,7 +604,7 @@ HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() { sp<V1_3::IVibrator> hal = getHal(); auto amplitudeResult = hal->supportsAmplitudeControl(); if (!amplitudeResult.isOk()) { - return HalResult<Capabilities>::fromReturn(amplitudeResult, capabilities); + return HalResultFactory::fromReturn<Capabilities>(amplitudeResult, capabilities); } auto externalControlResult = hal->supportsExternalControl(); @@ -686,7 +619,7 @@ HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() { } } - return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities); + return HalResultFactory::fromReturn<Capabilities>(externalControlResult, capabilities); } // ------------------------------------------------------------------------------------------------- diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp index 0df0bfa19a..aa5b7fc86f 100644 --- a/services/vibratorservice/VibratorManagerHalController.cpp +++ b/services/vibratorservice/VibratorManagerHalController.cpp @@ -46,8 +46,6 @@ template <typename T> HalResult<T> ManagerHalController::processHalResult(HalResult<T> result, const char* functionName) { if (result.isFailed()) { ALOGE("VibratorManager HAL %s failed: %s", functionName, result.errorMessage()); - std::lock_guard<std::mutex> lock(mConnectedHalMutex); - mConnectedHal->tryReconnect(); } return result; } @@ -70,12 +68,16 @@ HalResult<T> ManagerHalController::apply(ManagerHalController::hal_fn<T>& halFn, hal = mConnectedHal; } - HalResult<T> ret = processHalResult(halFn(hal), functionName); - for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) { - ret = processHalResult(halFn(hal), functionName); + HalResult<T> result = processHalResult(halFn(hal), functionName); + for (int i = 0; i < MAX_RETRIES && result.shouldRetry(); i++) { + { + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + mConnectedHal->tryReconnect(); + } + result = processHalResult(halFn(hal), functionName); } - return ret; + return result; } // ------------------------------------------------------------------------------------------------- diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp index 6e660e7e41..13412667e0 100644 --- a/services/vibratorservice/VibratorManagerHalWrapper.cpp +++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp @@ -55,8 +55,8 @@ HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(i return HalResult<std::shared_ptr<HalController>>::ok(mController); } // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator. - return HalResult<std::shared_ptr<HalController>>::failed(MISSING_VIBRATOR_MESSAGE_PREFIX + - std::to_string(id)); + return HalResult<std::shared_ptr<HalController>>::failed( + (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str()); } HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) { @@ -75,10 +75,10 @@ HalResult<void> LegacyManagerHalWrapper::cancelSynced() { std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator( int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) { - std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=]() { + std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=, this]() { sp<Aidl::IVibrator> vibrator; auto result = this->getHal()->getVibrator(vibratorId, &vibrator); - return HalResult<sp<Aidl::IVibrator>>::fromStatus(result, vibrator); + return HalResultFactory::fromStatus<sp<Aidl::IVibrator>>(result, vibrator); }; auto result = reconnectFn(); if (!result.isOk()) { @@ -88,12 +88,12 @@ std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator( if (!vibrator) { return nullptr; } - return std::move(std::make_unique<AidlHalWrapper>(std::move(callbackScheduler), - std::move(vibrator), reconnectFn)); + return std::make_unique<AidlHalWrapper>(std::move(callbackScheduler), std::move(vibrator), + reconnectFn); } HalResult<void> AidlManagerHalWrapper::ping() { - return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder()); + return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder()); } void AidlManagerHalWrapper::tryReconnect() { @@ -112,8 +112,8 @@ HalResult<ManagerCapabilities> AidlManagerHalWrapper::getCapabilities() { } int32_t cap = 0; auto result = getHal()->getCapabilities(&cap); - auto ret = HalResult<ManagerCapabilities>::fromStatus(result, - static_cast<ManagerCapabilities>(cap)); + auto capabilities = static_cast<ManagerCapabilities>(cap); + auto ret = HalResultFactory::fromStatus<ManagerCapabilities>(result, capabilities); if (ret.isOk()) { // Cache copy of returned value. mCapabilities.emplace(ret.value()); @@ -129,7 +129,7 @@ HalResult<std::vector<int32_t>> AidlManagerHalWrapper::getVibratorIds() { } std::vector<int32_t> ids; auto result = getHal()->getVibratorIds(&ids); - auto ret = HalResult<std::vector<int32_t>>::fromStatus(result, ids); + auto ret = HalResultFactory::fromStatus<std::vector<int32_t>>(result, ids); if (ret.isOk()) { // Cache copy of returned value and the individual controllers. mVibratorIds.emplace(ret.value()); @@ -152,12 +152,12 @@ HalResult<std::shared_ptr<HalController>> AidlManagerHalWrapper::getVibrator(int if (it != mVibrators.end()) { return HalResult<std::shared_ptr<HalController>>::ok(it->second); } - return HalResult<std::shared_ptr<HalController>>::failed(MISSING_VIBRATOR_MESSAGE_PREFIX + - std::to_string(id)); + return HalResult<std::shared_ptr<HalController>>::failed( + (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str()); } HalResult<void> AidlManagerHalWrapper::prepareSynced(const std::vector<int32_t>& ids) { - auto ret = HalResult<void>::fromStatus(getHal()->prepareSynced(ids)); + auto ret = HalResultFactory::fromStatus(getHal()->prepareSynced(ids)); if (ret.isOk()) { // Force reload of all vibrator controllers that were prepared for a sync operation here. // This will trigger calls to getVibrator(id) on each controller, so they can use the @@ -179,11 +179,11 @@ HalResult<void> AidlManagerHalWrapper::triggerSynced( bool supportsCallback = capabilities.isOk() && static_cast<int32_t>(capabilities.value() & ManagerCapabilities::TRIGGER_CALLBACK); auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; - return HalResult<void>::fromStatus(getHal()->triggerSynced(cb)); + return HalResultFactory::fromStatus(getHal()->triggerSynced(cb)); } HalResult<void> AidlManagerHalWrapper::cancelSynced() { - auto ret = HalResult<void>::fromStatus(getHal()->cancelSynced()); + auto ret = HalResultFactory::fromStatus(getHal()->cancelSynced()); if (ret.isOk()) { // Force reload of all vibrator controllers that were prepared for a sync operation before. // This will trigger calls to getVibrator(id) on each controller, so they can use the diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp index 971a0b960e..e11a809bc1 100644 --- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp +++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp @@ -152,6 +152,7 @@ BENCHMARK_WRAPPER(VibratorBench, off, { BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -180,6 +181,7 @@ BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -200,6 +202,7 @@ BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -221,6 +224,7 @@ BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -239,6 +243,7 @@ BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, { BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -331,9 +336,11 @@ protected: BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } if (!hasArgs(state)) { + state.SkipWithMessage("missing args"); return; } @@ -357,9 +364,11 @@ BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } if (!hasArgs(state)) { + state.SkipWithMessage("missing args"); return; } @@ -384,6 +393,7 @@ BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, { if (!hasArgs(state)) { + state.SkipWithMessage("missing args"); return; } @@ -441,9 +451,11 @@ protected: BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, { if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) { + state.SkipWithMessage("missing capability"); return; } if (!hasArgs(state)) { + state.SkipWithMessage("missing args"); return; } @@ -468,4 +480,4 @@ BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, { } }); -BENCHMARK_MAIN();
\ No newline at end of file +BENCHMARK_MAIN(); diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h index 6b73d17b0a..f97442ddee 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h @@ -64,7 +64,29 @@ public: */ Info getInfo() { static Info sDefaultInfo = InfoCache().get(); - return apply<Info>([](HalWrapper* hal) { return hal->getInfo(); }, sDefaultInfo, "getInfo"); + if (!init()) { + ALOGV("Skipped getInfo because Vibrator HAL is not available"); + return sDefaultInfo; + } + std::shared_ptr<HalWrapper> hal; + { + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + hal = mConnectedHal; + } + + for (int i = 0; i < MAX_RETRIES; i++) { + Info result = hal.get()->getInfo(); + result.logFailures(); + if (result.shouldRetry()) { + tryReconnect(); + } else { + return result; + } + } + + Info result = hal.get()->getInfo(); + result.logFailures(); + return result; } /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the @@ -72,7 +94,7 @@ public: */ template <typename T> HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, const char* functionName) { - return apply(halFn, HalResult<T>::unsupported(), functionName); + return doWithRetry<T>(halFn, HalResult<T>::unsupported(), functionName); } private: @@ -90,7 +112,8 @@ private: * function name is for logging purposes. */ template <typename T> - T apply(const HalFunction<T>& halFn, T defaultValue, const char* functionName) { + HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, HalResult<T> defaultValue, + const char* functionName) { if (!init()) { ALOGV("Skipped %s because Vibrator HAL is not available", functionName); return defaultValue; @@ -101,16 +124,22 @@ private: hal = mConnectedHal; } - for (int i = 0; i < MAX_RETRIES; i++) { - T result = halFn(hal.get()); - if (result.isFailedLogged(functionName)) { - tryReconnect(); - } else { - return result; - } + HalResult<T> result = doOnce(hal.get(), halFn, functionName); + for (int i = 0; i < MAX_RETRIES && result.shouldRetry(); i++) { + tryReconnect(); + result = doOnce(hal.get(), halFn, functionName); } + return result; + } - return halFn(hal.get()); + template <typename T> + HalResult<T> doOnce(HalWrapper* hal, const HalFunction<HalResult<T>>& halFn, + const char* functionName) { + HalResult<T> result = halFn(hal); + if (result.isFailed()) { + ALOGE("Vibrator HAL %s failed: %s", functionName, result.errorMessage()); + } + return result; } }; diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h index d2cc9ad7df..39c4eb441e 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -31,101 +31,154 @@ namespace vibrator { // ------------------------------------------------------------------------------------------------- +// Base class to represent a generic result of a call to the Vibrator HAL wrapper. +class BaseHalResult { +public: + bool isOk() const { return mStatus == SUCCESS; } + bool isFailed() const { return mStatus == FAILED; } + bool isUnsupported() const { return mStatus == UNSUPPORTED; } + bool shouldRetry() const { return isFailed() && mDeadObject; } + const char* errorMessage() const { return mErrorMessage.c_str(); } + +protected: + enum Status { SUCCESS, UNSUPPORTED, FAILED }; + Status mStatus; + std::string mErrorMessage; + bool mDeadObject; + + explicit BaseHalResult(Status status, const char* errorMessage = "", bool deadObject = false) + : mStatus(status), mErrorMessage(errorMessage), mDeadObject(deadObject) {} + virtual ~BaseHalResult() = default; +}; + // Result of a call to the Vibrator HAL wrapper, holding data if successful. template <typename T> -class HalResult { +class HalResult : public BaseHalResult { public: static HalResult<T> ok(T value) { return HalResult(value); } - static HalResult<T> failed(std::string msg) { - return HalResult(std::move(msg), /* unsupported= */ false); - } - static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); } - - static HalResult<T> fromStatus(binder::Status status, T data) { - if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION || - status.transactionError() == android::UNKNOWN_TRANSACTION) { - // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is - // the same as the operation being unsupported by this HAL. Should not retry. - return HalResult<T>::unsupported(); - } - if (status.isOk()) { - return HalResult<T>::ok(data); - } - return HalResult<T>::failed(status.toString8().c_str()); + static HalResult<T> unsupported() { return HalResult(Status::UNSUPPORTED); } + static HalResult<T> failed(const char* msg) { return HalResult(Status::FAILED, msg); } + static HalResult<T> transactionFailed(const char* msg) { + return HalResult(Status::FAILED, msg, /* deadObject= */ true); } - static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data); - - template <typename R> - static HalResult<T> fromReturn(hardware::Return<R>& ret, T data); - - template <typename R> - static HalResult<T> fromReturn(hardware::Return<R>& ret, - hardware::vibrator::V1_0::Status status, T data); // This will throw std::bad_optional_access if this result is not ok. const T& value() const { return mValue.value(); } const T valueOr(T&& defaultValue) const { return mValue.value_or(defaultValue); } - bool isOk() const { return !mUnsupported && mValue.has_value(); } - bool isFailed() const { return !mUnsupported && !mValue.has_value(); } - bool isUnsupported() const { return mUnsupported; } - const char* errorMessage() const { return mErrorMessage.c_str(); } - bool isFailedLogged(const char* functionNameForLogging) const { - if (isFailed()) { - ALOGE("Vibrator HAL %s failed: %s", functionNameForLogging, errorMessage()); - return true; - } - return false; - } private: std::optional<T> mValue; - std::string mErrorMessage; - bool mUnsupported; explicit HalResult(T value) - : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {} - explicit HalResult(std::string errorMessage, bool unsupported) - : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {} + : BaseHalResult(Status::SUCCESS), mValue(std::make_optional(value)) {} + explicit HalResult(Status status, const char* errorMessage = "", bool deadObject = false) + : BaseHalResult(status, errorMessage, deadObject), mValue() {} }; // Empty result of a call to the Vibrator HAL wrapper. template <> -class HalResult<void> { +class HalResult<void> : public BaseHalResult { public: - static HalResult<void> ok() { return HalResult(); } - static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); } - static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); } + static HalResult<void> ok() { return HalResult(Status::SUCCESS); } + static HalResult<void> unsupported() { return HalResult(Status::UNSUPPORTED); } + static HalResult<void> failed(const char* msg) { return HalResult(Status::FAILED, msg); } + static HalResult<void> transactionFailed(const char* msg) { + return HalResult(Status::FAILED, msg, /* deadObject= */ true); + } - static HalResult<void> fromStatus(status_t status); - static HalResult<void> fromStatus(binder::Status status); - static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status); +private: + explicit HalResult(Status status, const char* errorMessage = "", bool deadObject = false) + : BaseHalResult(status, errorMessage, deadObject) {} +}; + +// ------------------------------------------------------------------------------------------------- + +// Factory functions that convert failed HIDL/AIDL results into HalResult instances. +// Implementation of static template functions needs to be in this header file for the linker. +class HalResultFactory { +public: + template <typename T> + static HalResult<T> fromStatus(binder::Status status, T data) { + return status.isOk() ? HalResult<T>::ok(data) : fromFailedStatus<T>(status); + } + + template <typename T> + static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data) { + return (status == hardware::vibrator::V1_0::Status::OK) ? HalResult<T>::ok(data) + : fromFailedStatus<T>(status); + } + + template <typename T, typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) { + return ret.isOk() ? HalResult<T>::ok(data) : fromFailedReturn<T, R>(ret); + } + + template <typename T, typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, + hardware::vibrator::V1_0::Status status, T data) { + return ret.isOk() ? fromStatus<T>(status, data) : fromFailedReturn<T, R>(ret); + } + + static HalResult<void> fromStatus(status_t status) { + return (status == android::OK) ? HalResult<void>::ok() : fromFailedStatus<void>(status); + } + + static HalResult<void> fromStatus(binder::Status status) { + return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(status); + } + + static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status) { + return (status == hardware::vibrator::V1_0::Status::OK) ? HalResult<void>::ok() + : fromFailedStatus<void>(status); + } template <typename R> - static HalResult<void> fromReturn(hardware::Return<R>& ret); + static HalResult<void> fromReturn(hardware::Return<R>& ret) { + return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(ret); + } - bool isOk() const { return !mUnsupported && !mFailed; } - bool isFailed() const { return !mUnsupported && mFailed; } - bool isUnsupported() const { return mUnsupported; } - const char* errorMessage() const { return mErrorMessage.c_str(); } - bool isFailedLogged(const char* functionNameForLogging) const { - if (isFailed()) { - ALOGE("Vibrator HAL %s failed: %s", functionNameForLogging, errorMessage()); - return true; +private: + template <typename T> + static HalResult<T> fromFailedStatus(status_t status) { + auto msg = "status_t = " + statusToString(status); + return (status == android::DEAD_OBJECT) ? HalResult<T>::transactionFailed(msg.c_str()) + : HalResult<T>::failed(msg.c_str()); + } + + template <typename T> + static HalResult<T> fromFailedStatus(binder::Status status) { + if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION || + status.transactionError() == android::UNKNOWN_TRANSACTION) { + // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is + // the same as the operation being unsupported by this HAL. Should not retry. + return HalResult<T>::unsupported(); } - return false; + if (status.exceptionCode() == binder::Status::EX_TRANSACTION_FAILED) { + return HalResult<T>::transactionFailed(status.toString8().c_str()); + } + return HalResult<T>::failed(status.toString8().c_str()); } -private: - std::string mErrorMessage; - bool mFailed; - bool mUnsupported; + template <typename T> + static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status status) { + switch (status) { + case hardware::vibrator::V1_0::Status::UNSUPPORTED_OPERATION: + return HalResult<T>::unsupported(); + default: + auto msg = "android::hardware::vibrator::V1_0::Status = " + toString(status); + return HalResult<T>::failed(msg.c_str()); + } + } - explicit HalResult(bool unsupported = false) - : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {} - explicit HalResult(std::string errorMessage) - : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {} + template <typename T, typename R> + static HalResult<T> fromFailedReturn(hardware::Return<R>& ret) { + return ret.isDeadObject() ? HalResult<T>::transactionFailed(ret.description().c_str()) + : HalResult<T>::failed(ret.description().c_str()); + } }; +// ------------------------------------------------------------------------------------------------- + class HalCallbackWrapper : public hardware::vibrator::BnVibratorCallback { public: HalCallbackWrapper(std::function<void()> completionCallback) @@ -192,21 +245,44 @@ public: const HalResult<float> qFactor; const HalResult<std::vector<float>> maxAmplitudes; - bool isFailedLogged(const char*) const { - return capabilities.isFailedLogged("getCapabilities") || - supportedEffects.isFailedLogged("getSupportedEffects") || - supportedBraking.isFailedLogged("getSupportedBraking") || - supportedPrimitives.isFailedLogged("getSupportedPrimitives") || - primitiveDurations.isFailedLogged("getPrimitiveDuration") || - primitiveDelayMax.isFailedLogged("getPrimitiveDelayMax") || - pwlePrimitiveDurationMax.isFailedLogged("getPwlePrimitiveDurationMax") || - compositionSizeMax.isFailedLogged("getCompositionSizeMax") || - pwleSizeMax.isFailedLogged("getPwleSizeMax") || - minFrequency.isFailedLogged("getMinFrequency") || - resonantFrequency.isFailedLogged("getResonantFrequency") || - frequencyResolution.isFailedLogged("getFrequencyResolution") || - qFactor.isFailedLogged("getQFactor") || - maxAmplitudes.isFailedLogged("getMaxAmplitudes"); + void logFailures() const { + logFailure<Capabilities>(capabilities, "getCapabilities"); + logFailure<std::vector<hardware::vibrator::Effect>>(supportedEffects, + "getSupportedEffects"); + logFailure<std::vector<hardware::vibrator::Braking>>(supportedBraking, + "getSupportedBraking"); + logFailure<std::vector<hardware::vibrator::CompositePrimitive>>(supportedPrimitives, + "getSupportedPrimitives"); + logFailure<std::vector<std::chrono::milliseconds>>(primitiveDurations, + "getPrimitiveDuration"); + logFailure<std::chrono::milliseconds>(primitiveDelayMax, "getPrimitiveDelayMax"); + logFailure<std::chrono::milliseconds>(pwlePrimitiveDurationMax, + "getPwlePrimitiveDurationMax"); + logFailure<int32_t>(compositionSizeMax, "getCompositionSizeMax"); + logFailure<int32_t>(pwleSizeMax, "getPwleSizeMax"); + logFailure<float>(minFrequency, "getMinFrequency"); + logFailure<float>(resonantFrequency, "getResonantFrequency"); + logFailure<float>(frequencyResolution, "getFrequencyResolution"); + logFailure<float>(qFactor, "getQFactor"); + logFailure<std::vector<float>>(maxAmplitudes, "getMaxAmplitudes"); + } + + bool shouldRetry() const { + return capabilities.shouldRetry() || supportedEffects.shouldRetry() || + supportedBraking.shouldRetry() || supportedPrimitives.shouldRetry() || + primitiveDurations.shouldRetry() || primitiveDelayMax.shouldRetry() || + pwlePrimitiveDurationMax.shouldRetry() || compositionSizeMax.shouldRetry() || + pwleSizeMax.shouldRetry() || minFrequency.shouldRetry() || + resonantFrequency.shouldRetry() || frequencyResolution.shouldRetry() || + qFactor.shouldRetry() || maxAmplitudes.shouldRetry(); + } + +private: + template <typename T> + void logFailure(HalResult<T> result, const char* functionName) const { + if (result.isFailed()) { + ALOGE("Vibrator HAL %s failed: %s", functionName, result.errorMessage()); + } } }; @@ -230,27 +306,29 @@ public: } private: + // Create a transaction failed results as default so we can retry on the first time we get them. static const constexpr char* MSG = "never loaded"; - HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::failed(MSG); + HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::transactionFailed(MSG); HalResult<std::vector<hardware::vibrator::Effect>> mSupportedEffects = - HalResult<std::vector<hardware::vibrator::Effect>>::failed(MSG); + HalResult<std::vector<hardware::vibrator::Effect>>::transactionFailed(MSG); HalResult<std::vector<hardware::vibrator::Braking>> mSupportedBraking = - HalResult<std::vector<hardware::vibrator::Braking>>::failed(MSG); + HalResult<std::vector<hardware::vibrator::Braking>>::transactionFailed(MSG); HalResult<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives = - HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::failed(MSG); + HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::transactionFailed(MSG); HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations = - HalResult<std::vector<std::chrono::milliseconds>>::failed(MSG); + HalResult<std::vector<std::chrono::milliseconds>>::transactionFailed(MSG); HalResult<std::chrono::milliseconds> mPrimitiveDelayMax = - HalResult<std::chrono::milliseconds>::failed(MSG); + HalResult<std::chrono::milliseconds>::transactionFailed(MSG); HalResult<std::chrono::milliseconds> mPwlePrimitiveDurationMax = - HalResult<std::chrono::milliseconds>::failed(MSG); - HalResult<int32_t> mCompositionSizeMax = HalResult<int>::failed(MSG); - HalResult<int32_t> mPwleSizeMax = HalResult<int>::failed(MSG); - HalResult<float> mMinFrequency = HalResult<float>::failed(MSG); - HalResult<float> mResonantFrequency = HalResult<float>::failed(MSG); - HalResult<float> mFrequencyResolution = HalResult<float>::failed(MSG); - HalResult<float> mQFactor = HalResult<float>::failed(MSG); - HalResult<std::vector<float>> mMaxAmplitudes = HalResult<std::vector<float>>::failed(MSG); + HalResult<std::chrono::milliseconds>::transactionFailed(MSG); + HalResult<int32_t> mCompositionSizeMax = HalResult<int>::transactionFailed(MSG); + HalResult<int32_t> mPwleSizeMax = HalResult<int>::transactionFailed(MSG); + HalResult<float> mMinFrequency = HalResult<float>::transactionFailed(MSG); + HalResult<float> mResonantFrequency = HalResult<float>::transactionFailed(MSG); + HalResult<float> mFrequencyResolution = HalResult<float>::transactionFailed(MSG); + HalResult<float> mQFactor = HalResult<float>::transactionFailed(MSG); + HalResult<std::vector<float>> mMaxAmplitudes = + HalResult<std::vector<float>>::transactionFailed(MSG); friend class HalWrapper; }; diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp index 8e77bc57f3..9b95d74899 100644 --- a/services/vibratorservice/test/VibratorHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp @@ -107,17 +107,38 @@ TEST_F(VibratorHalControllerTest, TestInit) { ASSERT_EQ(1, mConnectCounter); } -TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnAnyFailure) { +TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnTransactionFailure) { EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal()) .Times(Exactly(2)) - .WillOnce(Return(vibrator::HalResult<vibrator::Capabilities>::failed("message"))) + .WillOnce(Return(vibrator::HalResult<vibrator::Capabilities>::transactionFailed("msg"))) .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::ok( vibrator::Capabilities::ON_CALLBACK))); auto result = mController->getInfo(); - ASSERT_FALSE(result.capabilities.isFailed()); + ASSERT_TRUE(result.capabilities.isOk()); + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestGetInfoDoesNotRetryOnOperationFailure) { + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0)); + EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal()) + .Times(Exactly(1)) + .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::failed("msg"))); + auto result = mController->getInfo(); + ASSERT_TRUE(result.capabilities.isFailed()); + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestGetInfoDoesNotRetryOnUnsupported) { + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0)); + EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal()) + .Times(Exactly(1)) + .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::unsupported())); + + auto result = mController->getInfo(); + ASSERT_TRUE(result.capabilities.isUnsupported()); ASSERT_EQ(1, mConnectCounter); } @@ -128,48 +149,54 @@ TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) { auto result = mController->doWithRetry<void>(ON_FN, "on"); ASSERT_TRUE(result.isOk()); - ASSERT_EQ(1, mConnectCounter); } -TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) { +TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoesNotResetHalConnection) { + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0)); EXPECT_CALL(*mMockHal.get(), off()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult<void>::unsupported())); - ASSERT_EQ(0, mConnectCounter); auto result = mController->doWithRetry<void>(OFF_FN, "off"); ASSERT_TRUE(result.isUnsupported()); ASSERT_EQ(1, mConnectCounter); } -TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) { +TEST_F(VibratorHalControllerTest, TestOperationFailedApiResultDoesNotResetHalConnection) { + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0)); EXPECT_CALL(*mMockHal.get(), on(_, _)) - .Times(Exactly(2)) + .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); - EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); - ASSERT_EQ(0, mConnectCounter); + auto result = mController->doWithRetry<void>(ON_FN, "on"); + ASSERT_TRUE(result.isFailed()); + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestTransactionFailedApiResultResetsHalConnection) { + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), on(_, _)) + .Times(Exactly(2)) + .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message"))); auto result = mController->doWithRetry<void>(ON_FN, "on"); ASSERT_TRUE(result.isFailed()); ASSERT_EQ(1, mConnectCounter); } -TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) { +TEST_F(VibratorHalControllerTest, TestTransactionFailedApiResultReturnsSuccessAfterRetries) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) - .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); + .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message"))); EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult<void>::ok())); } - ASSERT_EQ(0, mConnectCounter); - auto result = mController->doWithRetry<void>(PING_FN, "ping"); ASSERT_TRUE(result.isOk()); ASSERT_EQ(1, mConnectCounter); @@ -221,11 +248,11 @@ TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) { }); EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) - .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); + .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message"))); EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) - .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); + .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message"))); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp index e5fbbaeac9..11a8b66968 100644 --- a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp @@ -98,8 +98,10 @@ protected: .WillRepeatedly(Return(voidResult)); if (cardinality > 1) { - // One reconnection call after each failure. - EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * cardinality)); + // One reconnection for each retry. + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * (cardinality - 1))); + } else { + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0)); } } }; @@ -141,14 +143,12 @@ TEST_F(VibratorManagerHalControllerTest, TestApiCallsAreForwardedToHal) { ASSERT_EQ(1, mConnectCounter); } -TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) { +TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoesNotResetHalConnection) { setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::unsupported(), vibrator::HalResult<vibrator::ManagerCapabilities>::unsupported(), vibrator::HalResult<std::vector<int32_t>>::unsupported(), vibrator::HalResult<std::shared_ptr<HalController>>::unsupported()); - ASSERT_EQ(0, mConnectCounter); - ASSERT_TRUE(mController->ping().isUnsupported()); ASSERT_TRUE(mController->getCapabilities().isUnsupported()); ASSERT_TRUE(mController->getVibratorIds().isUnsupported()); @@ -160,13 +160,28 @@ TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoNotResetHalCo ASSERT_EQ(1, mConnectCounter); } -TEST_F(VibratorManagerHalControllerTest, TestFailedApiResultResetsHalConnection) { - setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::failed("message"), - vibrator::HalResult<vibrator::ManagerCapabilities>::failed("message"), - vibrator::HalResult<std::vector<int32_t>>::failed("message"), - vibrator::HalResult<std::shared_ptr<HalController>>::failed("message")); +TEST_F(VibratorManagerHalControllerTest, TestOperationFailedApiResultDoesNotResetHalConnection) { + setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::failed("msg"), + vibrator::HalResult<vibrator::ManagerCapabilities>::failed("msg"), + vibrator::HalResult<std::vector<int32_t>>::failed("msg"), + vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg")); - ASSERT_EQ(0, mConnectCounter); + ASSERT_TRUE(mController->ping().isFailed()); + ASSERT_TRUE(mController->getCapabilities().isFailed()); + ASSERT_TRUE(mController->getVibratorIds().isFailed()); + ASSERT_TRUE(mController->getVibrator(VIBRATOR_ID).isFailed()); + ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed()); + ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed()); + ASSERT_TRUE(mController->cancelSynced().isFailed()); + + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorManagerHalControllerTest, TestTransactionFailedApiResultResetsHalConnection) { + setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::transactionFailed("m"), + vibrator::HalResult<vibrator::ManagerCapabilities>::transactionFailed("m"), + vibrator::HalResult<std::vector<int32_t>>::transactionFailed("m"), + vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m")); ASSERT_TRUE(mController->ping().isFailed()); ASSERT_TRUE(mController->getCapabilities().isFailed()); @@ -184,14 +199,13 @@ TEST_F(VibratorManagerHalControllerTest, TestFailedApiResultReturnsSuccessAfterR InSequence seq; EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) - .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); + .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message"))); EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult<void>::ok())); } - ASSERT_EQ(0, mConnectCounter); ASSERT_TRUE(mController->ping().isOk()); ASSERT_EQ(1, mConnectCounter); } diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp index 1593cb18ec..dffc281fa1 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp @@ -254,13 +254,14 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorRecoversVibratorPointer EXPECT_CALL(*mMockHal.get(), getVibrator(Eq(kVibratorId), _)) .Times(Exactly(3)) .WillOnce(DoAll(SetArgPointee<1>(nullptr), - Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))) + Return(Status::fromExceptionCode( + Status::Exception::EX_TRANSACTION_FAILED)))) .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status()))); EXPECT_CALL(*mMockVibrator.get(), off()) .Times(Exactly(3)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_TRANSACTION_FAILED))) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_TRANSACTION_FAILED))) .WillRepeatedly(Return(Status())); // Get vibrator controller is successful even if first getVibrator. diff --git a/vulkan/Android.bp b/vulkan/Android.bp index 33599ea35b..2bf905c654 100644 --- a/vulkan/Android.bp +++ b/vulkan/Android.bp @@ -42,4 +42,5 @@ subdirs = [ "nulldrv", "libvulkan", "vkjson", + "vkprofiles", ] diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 0e45d2d8c4..81fd1185b6 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -340,8 +340,9 @@ void Hal::UnloadBuiltinDriver() { ALOGD("Unload builtin Vulkan driver."); // Close the opened device - ALOG_ASSERT(!hal_.dev_->common.close(hal_.dev_->common), - "hw_device_t::close() failed."); + int err = hal_.dev_->common.close( + const_cast<struct hw_device_t*>(&hal_.dev_->common)); + ALOG_ASSERT(!err, "hw_device_t::close() failed."); // Close the opened shared library in the hw_module_t android_unload_sphal_library(hal_.dev_->common.module->dso); diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 0df5e77181..13141933ac 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "vulkan/vulkan_core.h" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <aidl/android/hardware/graphics/common/Dataspace.h> @@ -158,6 +159,25 @@ int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) { } } +const static VkColorSpaceKHR colorSpaceSupportedByVkEXTSwapchainColorspace[] = { + VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT, + VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT, + VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT, + VK_COLOR_SPACE_BT709_LINEAR_EXT, + VK_COLOR_SPACE_BT709_NONLINEAR_EXT, + VK_COLOR_SPACE_BT2020_LINEAR_EXT, + VK_COLOR_SPACE_HDR10_ST2084_EXT, + VK_COLOR_SPACE_HDR10_HLG_EXT, + VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT, + VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT, + VK_COLOR_SPACE_PASS_THROUGH_EXT, + VK_COLOR_SPACE_DCI_P3_LINEAR_EXT}; + +const static VkColorSpaceKHR + colorSpaceSupportedByVkEXTSwapchainColorspaceOnFP16SurfaceOnly[] = { + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, + VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT}; + class TimingInfo { public: TimingInfo(const VkPresentTimeGOOGLE* qp, uint64_t nativeFrameId) @@ -745,16 +765,22 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, }; if (colorspace_ext) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace(colorSpace, GetNativePixelFormat( + VK_FORMAT_R8G8B8A8_UNORM)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R8G8B8A8_UNORM, colorSpace}); + } + + if (GetNativeDataspace(colorSpace, GetNativePixelFormat( + VK_FORMAT_R8G8B8A8_SRGB)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R8G8B8A8_SRGB, colorSpace}); + } + } } // NOTE: Any new formats that are added must be coordinated across different @@ -766,9 +792,16 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R5G6B5_UNORM_PACK16, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace( + colorSpace, + GetNativePixelFormat(VK_FORMAT_R5G6B5_UNORM_PACK16)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R5G6B5_UNORM_PACK16, colorSpace}); + } + } } } @@ -777,15 +810,28 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace( + colorSpace, + GetNativePixelFormat(VK_FORMAT_R16G16B16A16_SFLOAT)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R16G16B16A16_SFLOAT, colorSpace}); + } + } + + for ( + VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspaceOnFP16SurfaceOnly) { + if (GetNativeDataspace( + colorSpace, + GetNativePixelFormat(VK_FORMAT_R16G16B16A16_SFLOAT)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R16G16B16A16_SFLOAT, colorSpace}); + } + } } } @@ -795,12 +841,16 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, - VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace( + colorSpace, GetNativePixelFormat( + VK_FORMAT_A2B10G10R10_UNORM_PACK32)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_A2B10G10R10_UNORM_PACK32, colorSpace}); + } + } } } @@ -833,12 +883,18 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, - VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace( + colorSpace, + GetNativePixelFormat( + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + colorSpace}); + } + } } } @@ -1369,197 +1425,221 @@ static VkResult getProducerUsage(const VkDevice& device, const VkPhysicalDevice& pdev = GetData(device).driver_physical_device; const InstanceData& instance_data = GetData(pdev); const InstanceDriverTable& instance_dispatch = instance_data.driver; - if (!instance_dispatch.GetPhysicalDeviceImageFormatProperties2 && - !instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) { - uint64_t native_usage = 0; - void* usage_info_pNext = nullptr; - VkResult result; + if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2 || + instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) { + // Look through the create_info pNext chain passed to createSwapchainKHR + // for an image compression control struct. + // if one is found AND the appropriate extensions are enabled, create a + // VkImageCompressionControlEXT structure to pass on to + // GetPhysicalDeviceImageFormatProperties2 + void* compression_control_pNext = nullptr; VkImageCompressionControlEXT image_compression = {}; - const auto& dispatch = GetData(device).driver; - if (dispatch.GetSwapchainGrallocUsage4ANDROID) { - ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID"); - VkGrallocUsageInfo2ANDROID gralloc_usage_info = {}; - gralloc_usage_info.sType = - VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID; - gralloc_usage_info.format = create_info->imageFormat; - gralloc_usage_info.imageUsage = create_info->imageUsage; - gralloc_usage_info.swapchainImageUsage = swapchain_image_usage; - - // Look through the pNext chain for an image compression control struct - // if one is found AND the appropriate extensions are enabled, - // append it to be the gralloc usage pNext chain - const VkSwapchainCreateInfoKHR* create_infos = create_info; - while (create_infos->pNext) { - create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>( - create_infos->pNext); - switch (create_infos->sType) { - case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { - const VkImageCompressionControlEXT* compression_infos = - reinterpret_cast<const VkImageCompressionControlEXT*>( - create_infos); - image_compression = *compression_infos; - image_compression.pNext = nullptr; - usage_info_pNext = &image_compression; - } break; - - default: - // Ignore all other info structs - break; - } + const VkSwapchainCreateInfoKHR* create_infos = create_info; + while (create_infos->pNext) { + create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext); + switch (create_infos->sType) { + case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { + const VkImageCompressionControlEXT* compression_infos = + reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos); + image_compression = *compression_infos; + image_compression.pNext = nullptr; + compression_control_pNext = &image_compression; + } break; + default: + // Ignore all other info structs + break; } - gralloc_usage_info.pNext = usage_info_pNext; + } - result = dispatch.GetSwapchainGrallocUsage4ANDROID( - device, &gralloc_usage_info, &native_usage); - ATRACE_END(); - if (result != VK_SUCCESS) { - ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result); - return VK_ERROR_SURFACE_LOST_KHR; - } - } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) { - ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID"); - VkGrallocUsageInfoANDROID gralloc_usage_info = {}; - gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID; - gralloc_usage_info.format = create_info->imageFormat; - gralloc_usage_info.imageUsage = create_info->imageUsage; - - // Look through the pNext chain for an image compression control struct - // if one is found AND the appropriate extensions are enabled, - // append it to be the gralloc usage pNext chain - const VkSwapchainCreateInfoKHR* create_infos = create_info; - while (create_infos->pNext) { - create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>( - create_infos->pNext); - switch (create_infos->sType) { - case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { - const VkImageCompressionControlEXT* compression_infos = - reinterpret_cast<const VkImageCompressionControlEXT*>( - create_infos); - image_compression = *compression_infos; - image_compression.pNext = nullptr; - usage_info_pNext = &image_compression; - } break; + // call GetPhysicalDeviceImageFormatProperties2KHR + VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, + .pNext = compression_control_pNext, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, + }; - default: - // Ignore all other info structs - break; - } - } - gralloc_usage_info.pNext = usage_info_pNext; + // AHB does not have an sRGB format so we can't pass it to GPDIFP + // We need to convert the format to unorm if it is srgb + VkFormat format = create_info->imageFormat; + if (format == VK_FORMAT_R8G8B8A8_SRGB) { + format = VK_FORMAT_R8G8B8A8_UNORM; + } - result = dispatch.GetSwapchainGrallocUsage3ANDROID( - device, &gralloc_usage_info, &native_usage); - ATRACE_END(); - if (result != VK_SUCCESS) { - ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result); - return VK_ERROR_SURFACE_LOST_KHR; - } - } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) { - uint64_t consumer_usage, producer_usage; - ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID"); - result = dispatch.GetSwapchainGrallocUsage2ANDROID( - device, create_info->imageFormat, create_info->imageUsage, - swapchain_image_usage, &consumer_usage, &producer_usage); - ATRACE_END(); + VkPhysicalDeviceImageFormatInfo2 image_format_info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .pNext = &external_image_format_info, + .format = format, + .type = VK_IMAGE_TYPE_2D, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = create_info->imageUsage, + .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u, + }; + + VkAndroidHardwareBufferUsageANDROID ahb_usage; + ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID; + ahb_usage.pNext = nullptr; + + VkImageFormatProperties2 image_format_properties; + image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; + image_format_properties.pNext = &ahb_usage; + + if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) { + VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2( + pdev, &image_format_info, &image_format_properties); if (result != VK_SUCCESS) { - ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result); + ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result); return VK_ERROR_SURFACE_LOST_KHR; } - native_usage = - convertGralloc1ToBufferUsage(producer_usage, consumer_usage); - } else if (dispatch.GetSwapchainGrallocUsageANDROID) { - ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID"); - int32_t legacy_usage = 0; - result = dispatch.GetSwapchainGrallocUsageANDROID( - device, create_info->imageFormat, create_info->imageUsage, - &legacy_usage); - ATRACE_END(); + } + else { + VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR( + pdev, &image_format_info, + &image_format_properties); if (result != VK_SUCCESS) { - ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result); + ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d", + result); return VK_ERROR_SURFACE_LOST_KHR; } - native_usage = static_cast<uint64_t>(legacy_usage); } - *producer_usage = native_usage; - - return VK_SUCCESS; - } - // Look through the create_info pNext chain passed to createSwapchainKHR - // for an image compression control struct. - // if one is found AND the appropriate extensions are enabled, create a - // VkImageCompressionControlEXT structure to pass on to GetPhysicalDeviceImageFormatProperties2 - void* compression_control_pNext = nullptr; - VkImageCompressionControlEXT image_compression = {}; - const VkSwapchainCreateInfoKHR* create_infos = create_info; - while (create_infos->pNext) { - create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext); - switch (create_infos->sType) { - case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { - const VkImageCompressionControlEXT* compression_infos = - reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos); - image_compression = *compression_infos; - image_compression.pNext = nullptr; - compression_control_pNext = &image_compression; - } break; - default: - // Ignore all other info structs - break; + // Determine if USAGE_FRONT_BUFFER is needed. + // GPDIFP2 has no means of using VkSwapchainImageUsageFlagsANDROID when + // querying for producer_usage. So androidHardwareBufferUsage will not + // contain USAGE_FRONT_BUFFER. We need to manually check for usage here. + if (!(swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID)) { + *producer_usage = ahb_usage.androidHardwareBufferUsage; + return VK_SUCCESS; } - } - // call GetPhysicalDeviceImageFormatProperties2KHR - VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, - .pNext = compression_control_pNext, - .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, - }; + // Check if USAGE_FRONT_BUFFER is supported for this swapchain + AHardwareBuffer_Desc ahb_desc = { + .width = create_info->imageExtent.width, + .height = create_info->imageExtent.height, + .layers = create_info->imageArrayLayers, + .format = create_info->imageFormat, + .usage = ahb_usage.androidHardwareBufferUsage | AHARDWAREBUFFER_USAGE_FRONT_BUFFER, + .stride = 0, // stride is always ignored when calling isSupported() + }; - // AHB does not have an sRGB format so we can't pass it to GPDIFP - // We need to convert the format to unorm if it is srgb - VkFormat format = create_info->imageFormat; - if (format == VK_FORMAT_R8G8B8A8_SRGB) { - format = VK_FORMAT_R8G8B8A8_UNORM; + // If FRONT_BUFFER is not supported, + // then we need to call GetSwapchainGrallocUsageXAndroid below + if (AHardwareBuffer_isSupported(&ahb_desc)) { + *producer_usage = ahb_usage.androidHardwareBufferUsage; + *producer_usage |= AHARDWAREBUFFER_USAGE_FRONT_BUFFER; + return VK_SUCCESS; + } } - VkPhysicalDeviceImageFormatInfo2 image_format_info = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, - .pNext = &external_image_format_info, - .format = format, - .type = VK_IMAGE_TYPE_2D, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = create_info->imageUsage, - .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u, - }; + uint64_t native_usage = 0; + void* usage_info_pNext = nullptr; + VkResult result; + VkImageCompressionControlEXT image_compression = {}; + const auto& dispatch = GetData(device).driver; + if (dispatch.GetSwapchainGrallocUsage4ANDROID) { + ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID"); + VkGrallocUsageInfo2ANDROID gralloc_usage_info = {}; + gralloc_usage_info.sType = + VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID; + gralloc_usage_info.format = create_info->imageFormat; + gralloc_usage_info.imageUsage = create_info->imageUsage; + gralloc_usage_info.swapchainImageUsage = swapchain_image_usage; + + // Look through the pNext chain for an image compression control struct + // if one is found AND the appropriate extensions are enabled, + // append it to be the gralloc usage pNext chain + const VkSwapchainCreateInfoKHR* create_infos = create_info; + while (create_infos->pNext) { + create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>( + create_infos->pNext); + switch (create_infos->sType) { + case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { + const VkImageCompressionControlEXT* compression_infos = + reinterpret_cast<const VkImageCompressionControlEXT*>( + create_infos); + image_compression = *compression_infos; + image_compression.pNext = nullptr; + usage_info_pNext = &image_compression; + } break; + + default: + // Ignore all other info structs + break; + } + } + gralloc_usage_info.pNext = usage_info_pNext; - VkAndroidHardwareBufferUsageANDROID ahb_usage; - ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID; - ahb_usage.pNext = nullptr; + result = dispatch.GetSwapchainGrallocUsage4ANDROID( + device, &gralloc_usage_info, &native_usage); + ATRACE_END(); + if (result != VK_SUCCESS) { + ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result); + return VK_ERROR_SURFACE_LOST_KHR; + } + } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) { + ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID"); + VkGrallocUsageInfoANDROID gralloc_usage_info = {}; + gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID; + gralloc_usage_info.format = create_info->imageFormat; + gralloc_usage_info.imageUsage = create_info->imageUsage; + + // Look through the pNext chain for an image compression control struct + // if one is found AND the appropriate extensions are enabled, + // append it to be the gralloc usage pNext chain + const VkSwapchainCreateInfoKHR* create_infos = create_info; + while (create_infos->pNext) { + create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>( + create_infos->pNext); + switch (create_infos->sType) { + case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { + const VkImageCompressionControlEXT* compression_infos = + reinterpret_cast<const VkImageCompressionControlEXT*>( + create_infos); + image_compression = *compression_infos; + image_compression.pNext = nullptr; + usage_info_pNext = &image_compression; + } break; - VkImageFormatProperties2 image_format_properties; - image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; - image_format_properties.pNext = &ahb_usage; + default: + // Ignore all other info structs + break; + } + } + gralloc_usage_info.pNext = usage_info_pNext; - if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) { - VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2( - pdev, &image_format_info, &image_format_properties); + result = dispatch.GetSwapchainGrallocUsage3ANDROID( + device, &gralloc_usage_info, &native_usage); + ATRACE_END(); if (result != VK_SUCCESS) { - ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result); + ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result); return VK_ERROR_SURFACE_LOST_KHR; } - } - else { - VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR( - pdev, &image_format_info, - &image_format_properties); + } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) { + uint64_t consumer_usage, producer_usage; + ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID"); + result = dispatch.GetSwapchainGrallocUsage2ANDROID( + device, create_info->imageFormat, create_info->imageUsage, + swapchain_image_usage, &consumer_usage, &producer_usage); + ATRACE_END(); if (result != VK_SUCCESS) { - ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d", - result); + ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result); return VK_ERROR_SURFACE_LOST_KHR; } + native_usage = + convertGralloc1ToBufferUsage(producer_usage, consumer_usage); + } else if (dispatch.GetSwapchainGrallocUsageANDROID) { + ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID"); + int32_t legacy_usage = 0; + result = dispatch.GetSwapchainGrallocUsageANDROID( + device, create_info->imageFormat, create_info->imageUsage, + &legacy_usage); + ATRACE_END(); + if (result != VK_SUCCESS) { + ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result); + return VK_ERROR_SURFACE_LOST_KHR; + } + native_usage = static_cast<uint64_t>(legacy_usage); } - - *producer_usage = ahb_usage.androidHardwareBufferUsage; + *producer_usage = native_usage; return VK_SUCCESS; } @@ -1762,6 +1842,8 @@ VkResult CreateSwapchainKHR(VkDevice device, } int query_value; + // TODO: Now that we are calling into GPDSC2 directly, this query may be redundant + // the call to std::max(min_buffer_count, num_images) may be redundant as well err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != android::OK || query_value < 0) { @@ -1778,12 +1860,33 @@ VkResult CreateSwapchainKHR(VkDevice device, // with extra images (which they can't actually use!). const uint32_t min_buffer_count = min_undequeued_buffers + 1; - uint32_t num_images; - if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) { - num_images = std::max(3u, create_info->minImageCount); - } else { - num_images = create_info->minImageCount; - } + // Call into GPDSC2 to get the minimum and maximum allowable buffer count for the surface of + // interest. This step is only necessary if the app requests a number of images + // (create_info->minImageCount) that is less or more than the surface capabilities. + // An app should be calling GPDSC2 and using those values to set create_info, but in the + // event that the app has hard-coded image counts an error can occur + VkSurfacePresentModeEXT present_mode = { + VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT, + nullptr, + create_info->presentMode + }; + VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, + &present_mode, + create_info->surface + }; + VkSurfaceCapabilities2KHR surface_capabilities2 = { + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, + nullptr, + {}, + }; + result = GetPhysicalDeviceSurfaceCapabilities2KHR(GetData(device).driver_physical_device, + &surface_info2, &surface_capabilities2); + + uint32_t num_images = create_info->minImageCount; + num_images = std::clamp(num_images, + surface_capabilities2.surfaceCapabilities.minImageCount, + surface_capabilities2.surfaceCapabilities.maxImageCount); const uint32_t buffer_count = std::max(min_buffer_count, num_images); err = native_window_set_buffer_count(window, buffer_count); diff --git a/vulkan/vkprofiles/Android.bp b/vulkan/vkprofiles/Android.bp new file mode 100644 index 0000000000..94850cc2e4 --- /dev/null +++ b/vulkan/vkprofiles/Android.bp @@ -0,0 +1,68 @@ +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_defaults { + name: "libvkprofiles_deps", + shared_libs: [ + "libvulkan", + ], +} + +cc_library_static { + name: "libvkprofiles", + defaults: [ + "libvkprofiles_deps", + ], + srcs: [ + "vkprofiles.cpp", + "generated/vulkan_profiles.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wimplicit-fallthrough", + ], + cppflags: [ + "-Wno-error=unused-parameter", + "-Wno-error=missing-braces", + "-Wno-sign-compare", + ], + export_include_dirs: [ + ".", + ], + export_shared_lib_headers: [ + "libvulkan", + ], +} + +cc_library_static { + name: "libvkprofiles_ndk", + srcs: [ + "vkprofiles.cpp", + "generated/vulkan_profiles.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wimplicit-fallthrough", + ], + cppflags: [ + "-Wno-error=unused-parameter", + "-Wno-error=missing-braces", + "-Wno-sign-compare", + ], + export_include_dirs: [ + ".", + ], + header_libs: [ + "vulkan_headers", + ], + sdk_version: "24", + stl: "libc++_static", +} diff --git a/vulkan/vkprofiles/README.md b/vulkan/vkprofiles/README.md new file mode 100644 index 0000000000..261089ae58 --- /dev/null +++ b/vulkan/vkprofiles/README.md @@ -0,0 +1,38 @@ + +Get a local copy of the Vulkan-Profiles repository (https://github.com/KhronosGroup/Vulkan-Profiles/) + +NOTE: If the Vulkan-Headers you need for generation is later than the one that exists in +`external/vulkan-headers`, then `external/vulkan-headers` will need to be updated to match. +These updates to `external/vulkan` need to be made in AOSP. Changes to `ndk_translation` may +need to be first made in internal-main. + +Run Vulkan-Profiles/scripts/gen_profiles_solutions.py in debug mode. + +Debug mode (at time of writing) requires a dedicated debug folder within the output-library location. +~/Vulkan-Profiles$ mkdir debug +~/Vulkan-Profiles$ python3 scripts/gen_profiles_solution.py --debug --registry ~/<PATH_TO_YOUR_ANDROID_REPO>/external/vulkan-headers/registry/vk.xml --input ~/android/main/frameworks/native/vulkan/vkprofiles/profiles/ --output-library-inc . --output-library-src . + +Take the generated vulkan_profiles.h and vulkan_profiles.cpp from the debug directory you just created. + +~/Vulkan-Profiles$ cp debug/vulkan_profiles.cpp <PATH_TO_YOUR_ANDROID_REPO>/frameworks/native/vulkan/vkprofile/generated/ +~/Vulkan-Profiles$ cp debug/vulkan_profiles.h <PATH_TO_YOUR_ANDROID_REPO>/frameworks/native/vulkan/vkprofile/generated/ + + +The files need to be modified to land. +1. Replace the generated license with the correct Android license +(https://cs.android.com/android/platform/superproject/main/+/main:development/docs/copyright-templates/c.txt). +Make sure to set the copyright to the current year. You should also remove the `This file is ***GENERATED***` part. +2. Add VK_USE_PLATFORM_ANDROID_KHR between the license and the first includes for vulkan_profiles.cpp +``` + */ + +#ifndef VK_USE_PLATFORM_ANDROID_KHR +#define VK_USE_PLATFORM_ANDROID_KHR +#endif + +#include ... +``` +3. Rewrite the includes so that `vulkan_profiles.h` is correctly included +4. Modify the #define `VP_DEBUG_MESSAGE_CALLBACK(MSG) ...` from "Profiles ERROR/WARNING" to "vkprofiles ERROR/WARNING" +5. You may need to modify the Android.bp to remove warnings as errors, e.g. `"-Wno-error=unused-parameter",` +6. Add `clang-format off` to the beginning and `clang-format on` to the end of the files diff --git a/vulkan/vkprofiles/generated/.clang-format b/vulkan/vkprofiles/generated/.clang-format new file mode 100644 index 0000000000..fa1d429c73 --- /dev/null +++ b/vulkan/vkprofiles/generated/.clang-format @@ -0,0 +1,3 @@ +# Take the outputted source files from gen_profiles_solution.py and don't format +DisableFormat: true +SortIncludes: Never diff --git a/vulkan/vkprofiles/generated/vulkan_profiles.cpp b/vulkan/vkprofiles/generated/vulkan_profiles.cpp new file mode 100644 index 0000000000..ce08b4e072 --- /dev/null +++ b/vulkan/vkprofiles/generated/vulkan_profiles.cpp @@ -0,0 +1,11257 @@ + +/* + * 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. + * + */ + +// clang-format off + +#ifndef VK_USE_PLATFORM_ANDROID_KHR +#define VK_USE_PLATFORM_ANDROID_KHR +#endif + +#include <cstddef> +#include <cstdarg> +#include <cstdio> +#include <cstring> +#include <cstdint> +#include <cassert> +#include <cmath> +#include <string> +#include <vector> +#include <algorithm> +#include <memory> +#include <map> +#include "vulkan_profiles.h" + +#include <cstdio> + +#ifndef VP_DEBUG_MESSAGE_CALLBACK +#if defined(ANDROID) || defined(__ANDROID__) +#include <android/log.h> +#define VP_DEBUG_MESSAGE_CALLBACK(MSG) __android_log_print(ANDROID_LOG_ERROR, "vkprofiles ERROR", "%s", MSG); \ + __android_log_print(ANDROID_LOG_DEBUG, "vkprofiles WARNING", "%s", MSG) +#else +#define VP_DEBUG_MESSAGE_CALLBACK(MSG) fprintf(stderr, "%s\n", MSG) +#endif +#else +void VP_DEBUG_MESSAGE_CALLBACK(const char*); +#endif + +#define VP_DEBUG_MSG(MSG) VP_DEBUG_MESSAGE_CALLBACK(MSG) +#define VP_DEBUG_MSGF(MSGFMT, ...) { char msg[1024]; snprintf(msg, sizeof(msg) - 1, (MSGFMT), __VA_ARGS__); VP_DEBUG_MESSAGE_CALLBACK(msg); } +#define VP_DEBUG_COND_MSG(COND, MSG) if (COND) VP_DEBUG_MSG(MSG) +#define VP_DEBUG_COND_MSGF(COND, MSGFMT, ...) if (COND) VP_DEBUG_MSGF(MSGFMT, __VA_ARGS__) + +#include <string> + +namespace detail { + +VPAPI_ATTR std::string vpGetDeviceAndDriverInfoString(VkPhysicalDevice physicalDevice, + PFN_vkGetPhysicalDeviceProperties2KHR pfnGetPhysicalDeviceProperties2) { + VkPhysicalDeviceDriverPropertiesKHR driverProps{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR }; + VkPhysicalDeviceProperties2KHR deviceProps{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, &driverProps }; + pfnGetPhysicalDeviceProperties2(physicalDevice, &deviceProps); + return std::string("deviceName=") + std::string(&deviceProps.properties.deviceName[0]) + + ", driverName=" + std::string(&driverProps.driverName[0]) + + ", driverInfo=" + std::string(&driverProps.driverInfo[0]); +} + +} + +namespace detail { + + +VPAPI_ATTR std::string FormatString(const char* message, ...) { + std::size_t const STRING_BUFFER(4096); + + assert(message != nullptr); + assert(strlen(message) >= 1 && strlen(message) < STRING_BUFFER); + + char buffer[STRING_BUFFER]; + va_list list; + + va_start(list, message); + vsnprintf(buffer, STRING_BUFFER, message, list); + va_end(list); + + return buffer; +} + +VPAPI_ATTR const void* vpGetStructure(const void* pNext, VkStructureType type) { + const VkBaseOutStructure* p = static_cast<const VkBaseOutStructure*>(pNext); + while (p != nullptr) { + if (p->sType == type) return p; + p = p->pNext; + } + return nullptr; +} + +VPAPI_ATTR void* vpGetStructure(void* pNext, VkStructureType type) { + VkBaseOutStructure* p = static_cast<VkBaseOutStructure*>(pNext); + while (p != nullptr) { + if (p->sType == type) return p; + p = p->pNext; + } + return nullptr; +} + +VPAPI_ATTR VkBaseOutStructure* vpExtractStructure(VkPhysicalDeviceFeatures2KHR* pFeatures, VkStructureType structureType) { + if (structureType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR) { + return nullptr; + } + + VkBaseOutStructure* current = reinterpret_cast<VkBaseOutStructure*>(pFeatures); + VkBaseOutStructure* previous = nullptr; + VkBaseOutStructure* found = nullptr; + + while (current != nullptr) { + if (structureType == current->sType) { + found = current; + if (previous != nullptr) { + previous->pNext = current->pNext; + } + current = nullptr; + } else { + previous = current; + current = current->pNext; + } + } + + if (found != nullptr) { + found->pNext = nullptr; + return found; + } else { + return nullptr; + } +} + +VPAPI_ATTR void GatherStructureTypes(std::vector<VkStructureType>& structureTypes, VkBaseOutStructure* pNext) { + while (pNext) { + if (std::find(structureTypes.begin(), structureTypes.end(), pNext->sType) == structureTypes.end()) { + structureTypes.push_back(pNext->sType); + } + + pNext = pNext->pNext; + } +} + +VPAPI_ATTR bool isMultiple(double source, double multiple) { + double mod = std::fmod(source, multiple); + return std::abs(mod) < 0.0001; +} + +VPAPI_ATTR bool isPowerOfTwo(double source) { + double mod = std::fmod(source, 1.0); + if (std::abs(mod) >= 0.0001) return false; + + std::uint64_t value = static_cast<std::uint64_t>(std::abs(source)); + return !(value & (value - static_cast<std::uint64_t>(1))); +} + +using PFN_vpStructFiller = void(*)(VkBaseOutStructure* p); +using PFN_vpStructComparator = bool(*)(VkBaseOutStructure* p); +using PFN_vpStructChainerCb = void(*)(VkBaseOutStructure* p, void* pUser); +using PFN_vpStructChainer = void(*)(VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb); + +struct VpFeatureDesc { + PFN_vpStructFiller pfnFiller; + PFN_vpStructComparator pfnComparator; +}; + +struct VpPropertyDesc { + PFN_vpStructFiller pfnFiller; + PFN_vpStructComparator pfnComparator; +}; + +struct VpQueueFamilyDesc { + PFN_vpStructFiller pfnFiller; + PFN_vpStructComparator pfnComparator; +}; + +struct VpFormatDesc { + VkFormat format; + PFN_vpStructFiller pfnFiller; + PFN_vpStructComparator pfnComparator; +}; + +struct VpStructChainerDesc { + PFN_vpStructChainer pfnFeature; + PFN_vpStructChainer pfnProperty; + PFN_vpStructChainer pfnQueueFamily; + PFN_vpStructChainer pfnFormat; +}; + +struct VpVariantDesc { + char blockName[VP_MAX_PROFILE_NAME_SIZE]; + + uint32_t instanceExtensionCount; + const VkExtensionProperties* pInstanceExtensions; + + uint32_t deviceExtensionCount; + const VkExtensionProperties* pDeviceExtensions; + + uint32_t featureStructTypeCount; + const VkStructureType* pFeatureStructTypes; + VpFeatureDesc feature; + + uint32_t propertyStructTypeCount; + const VkStructureType* pPropertyStructTypes; + VpPropertyDesc property; + + uint32_t queueFamilyStructTypeCount; + const VkStructureType* pQueueFamilyStructTypes; + uint32_t queueFamilyCount; + const VpQueueFamilyDesc* pQueueFamilies; + + uint32_t formatStructTypeCount; + const VkStructureType* pFormatStructTypes; + uint32_t formatCount; + const VpFormatDesc* pFormats; + + VpStructChainerDesc chainers; +}; + +struct VpCapabilitiesDesc { + uint32_t variantCount; + const VpVariantDesc* pVariants; +}; + +struct VpProfileDesc { + VpProfileProperties props; + uint32_t minApiVersion; + + const detail::VpVariantDesc* pMergedCapabilities; + + uint32_t requiredProfileCount; + const VpProfileProperties* pRequiredProfiles; + + uint32_t requiredCapabilityCount; + const VpCapabilitiesDesc* pRequiredCapabilities; + + uint32_t fallbackCount; + const VpProfileProperties* pFallbacks; +}; + +template <typename T> +VPAPI_ATTR bool vpCheckFlags(const T& actual, const uint64_t expected) { + return (actual & expected) == expected; +} +#ifdef VP_ANDROID_15_minimums +namespace VP_ANDROID_15_MINIMUMS { + +static const VkStructureType featureStructTypes[] = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG, +}; + +static const VkStructureType propertyStructTypes[] = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, +}; + +static const VkStructureType formatStructTypes[] = { + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, +}; + +namespace MUST { +static const VkExtensionProperties instanceExtensions[] = { + VkExtensionProperties{ VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME, 1 }, +}; + +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_ANDROID_EXTERNAL_FORMAT_RESOLVE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_4444_FORMATS_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_EXTERNAL_MEMORY_ACQUIRE_UNMODIFIED_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_16BIT_STORAGE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_MAINTENANCE_5_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* s = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + s->features.drawIndirectFirstInstance = VK_TRUE; + s->features.samplerAnisotropy = VK_TRUE; + s->features.shaderImageGatherExtended = VK_TRUE; + s->features.shaderStorageImageExtendedFormats = VK_TRUE; + s->features.shaderStorageImageReadWithoutFormat = VK_TRUE; + s->features.shaderStorageImageWriteWithoutFormat = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: { + VkPhysicalDeviceVulkan12Features* s = static_cast<VkPhysicalDeviceVulkan12Features*>(static_cast<void*>(p)); + s->shaderFloat16 = VK_TRUE; + s->shaderInt8 = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT: { + VkPhysicalDeviceCustomBorderColorFeaturesEXT* s = static_cast<VkPhysicalDeviceCustomBorderColorFeaturesEXT*>(static_cast<void*>(p)); + s->customBorderColors = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT: { + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT* s = static_cast<VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT*>(static_cast<void*>(p)); + s->primitiveTopologyListRestart = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT: { + VkPhysicalDeviceProvokingVertexFeaturesEXT* s = static_cast<VkPhysicalDeviceProvokingVertexFeaturesEXT*>(static_cast<void*>(p)); + s->provokingVertexLast = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT: { + VkPhysicalDeviceIndexTypeUint8FeaturesEXT* s = static_cast<VkPhysicalDeviceIndexTypeUint8FeaturesEXT*>(static_cast<void*>(p)); + s->indexTypeUint8 = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR: { + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR* s = static_cast<VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR*>(static_cast<void*>(p)); + s->vertexAttributeInstanceRateDivisor = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { + VkPhysicalDeviceSamplerYcbcrConversionFeatures* s = static_cast<VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(static_cast<void*>(p)); + s->samplerYcbcrConversion = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES: { + VkPhysicalDeviceShaderFloat16Int8Features* s = static_cast<VkPhysicalDeviceShaderFloat16Int8Features*>(static_cast<void*>(p)); + s->shaderFloat16 = VK_TRUE; + s->shaderInt8 = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES: { + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures* s = static_cast<VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures*>(static_cast<void*>(p)); + s->shaderSubgroupExtendedTypes = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES: { + VkPhysicalDevice8BitStorageFeatures* s = static_cast<VkPhysicalDevice8BitStorageFeatures*>(static_cast<void*>(p)); + s->storageBuffer8BitAccess = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: { + VkPhysicalDevice16BitStorageFeatures* s = static_cast<VkPhysicalDevice16BitStorageFeatures*>(static_cast<void*>(p)); + s->storageBuffer16BitAccess = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* prettify_VkPhysicalDeviceFeatures2KHR = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.drawIndirectFirstInstance == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.drawIndirectFirstInstance == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.drawIndirectFirstInstance == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.samplerAnisotropy == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.samplerAnisotropy == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.samplerAnisotropy == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderImageGatherExtended == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderImageGatherExtended == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderImageGatherExtended == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageExtendedFormats == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageExtendedFormats == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageExtendedFormats == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageReadWithoutFormat == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageReadWithoutFormat == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageReadWithoutFormat == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageWriteWithoutFormat == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageWriteWithoutFormat == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageWriteWithoutFormat == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: { + VkPhysicalDeviceVulkan12Features* prettify_VkPhysicalDeviceVulkan12Features = static_cast<VkPhysicalDeviceVulkan12Features*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceVulkan12Features->shaderFloat16 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceVulkan12Features->shaderFloat16 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceVulkan12Features::shaderFloat16 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceVulkan12Features->shaderInt8 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceVulkan12Features->shaderInt8 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceVulkan12Features::shaderInt8 == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT: { + VkPhysicalDeviceCustomBorderColorFeaturesEXT* prettify_VkPhysicalDeviceCustomBorderColorFeaturesEXT = static_cast<VkPhysicalDeviceCustomBorderColorFeaturesEXT*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceCustomBorderColorFeaturesEXT->customBorderColors == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceCustomBorderColorFeaturesEXT->customBorderColors == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceCustomBorderColorFeaturesEXT::customBorderColors == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT: { + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT* prettify_VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT = static_cast<VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT->primitiveTopologyListRestart == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT->primitiveTopologyListRestart == VK_TRUE), "Unsupported feature condition: VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT::primitiveTopologyListRestart == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT: { + VkPhysicalDeviceProvokingVertexFeaturesEXT* prettify_VkPhysicalDeviceProvokingVertexFeaturesEXT = static_cast<VkPhysicalDeviceProvokingVertexFeaturesEXT*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceProvokingVertexFeaturesEXT->provokingVertexLast == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProvokingVertexFeaturesEXT->provokingVertexLast == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceProvokingVertexFeaturesEXT::provokingVertexLast == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT: { + VkPhysicalDeviceIndexTypeUint8FeaturesEXT* prettify_VkPhysicalDeviceIndexTypeUint8FeaturesEXT = static_cast<VkPhysicalDeviceIndexTypeUint8FeaturesEXT*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceIndexTypeUint8FeaturesEXT->indexTypeUint8 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceIndexTypeUint8FeaturesEXT->indexTypeUint8 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceIndexTypeUint8FeaturesEXT::indexTypeUint8 == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR: { + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR* prettify_VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR = static_cast<VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR->vertexAttributeInstanceRateDivisor == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR->vertexAttributeInstanceRateDivisor == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR::vertexAttributeInstanceRateDivisor == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { + VkPhysicalDeviceSamplerYcbcrConversionFeatures* prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures = static_cast<VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures->samplerYcbcrConversion == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures->samplerYcbcrConversion == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES: { + VkPhysicalDeviceShaderFloat16Int8Features* prettify_VkPhysicalDeviceShaderFloat16Int8Features = static_cast<VkPhysicalDeviceShaderFloat16Int8Features*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceShaderFloat16Int8Features->shaderFloat16 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceShaderFloat16Int8Features->shaderFloat16 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceShaderFloat16Int8Features::shaderFloat16 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceShaderFloat16Int8Features->shaderInt8 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceShaderFloat16Int8Features->shaderInt8 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceShaderFloat16Int8Features::shaderInt8 == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES: { + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures* prettify_VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures = static_cast<VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures->shaderSubgroupExtendedTypes == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures->shaderSubgroupExtendedTypes == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures::shaderSubgroupExtendedTypes == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES: { + VkPhysicalDevice8BitStorageFeatures* prettify_VkPhysicalDevice8BitStorageFeatures = static_cast<VkPhysicalDevice8BitStorageFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDevice8BitStorageFeatures->storageBuffer8BitAccess == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDevice8BitStorageFeatures->storageBuffer8BitAccess == VK_TRUE), "Unsupported feature condition: VkPhysicalDevice8BitStorageFeatures::storageBuffer8BitAccess == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: { + VkPhysicalDevice16BitStorageFeatures* prettify_VkPhysicalDevice16BitStorageFeatures = static_cast<VkPhysicalDevice16BitStorageFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDevice16BitStorageFeatures->storageBuffer16BitAccess == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDevice16BitStorageFeatures->storageBuffer16BitAccess == VK_TRUE), "Unsupported feature condition: VkPhysicalDevice16BitStorageFeatures::storageBuffer16BitAccess == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR: { + VkPhysicalDeviceProperties2KHR* s = static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p)); + s->properties.limits.maxColorAttachments = 8; + s->properties.limits.maxPerStageDescriptorSampledImages = 128; + s->properties.limits.maxPerStageDescriptorSamplers = 128; + s->properties.limits.maxPerStageDescriptorStorageBuffers = 12; + s->properties.limits.maxPerStageDescriptorUniformBuffers = 13; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES: { + VkPhysicalDeviceVulkan11Properties* s = static_cast<VkPhysicalDeviceVulkan11Properties*>(static_cast<void*>(p)); + s->subgroupSupportedOperations |= (VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_VOTE_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR: { + VkPhysicalDeviceProperties2KHR* prettify_VkPhysicalDeviceProperties2KHR = static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxColorAttachments >= 8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxColorAttachments >= 8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxColorAttachments >= 8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSampledImages >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSampledImages >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorSampledImages >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSamplers >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSamplers >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorSamplers >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageBuffers >= 12); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageBuffers >= 12), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorStorageBuffers >= 12"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorUniformBuffers >= 13); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorUniformBuffers >= 13), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorUniformBuffers >= 13"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES: { + VkPhysicalDeviceVulkan11Properties* prettify_VkPhysicalDeviceVulkan11Properties = static_cast<VkPhysicalDeviceVulkan11Properties*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceVulkan11Properties->subgroupSupportedOperations, (VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_VOTE_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceVulkan11Properties->subgroupSupportedOperations, (VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_VOTE_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT))), "Unsupported properties condition: VkPhysicalDeviceVulkan11Properties::subgroupSupportedOperations contains (VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_VOTE_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT)"); + } break; + default: break; + } + return ret; + } +}; + +static const VpFormatDesc formatDesc[] = { + { + VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan12Features physicalDeviceVulkan12Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, nullptr }; + VkPhysicalDeviceCustomBorderColorFeaturesEXT physicalDeviceCustomBorderColorFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, &physicalDeviceVulkan12Features }; + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT physicalDevicePrimitiveTopologyListRestartFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, &physicalDeviceCustomBorderColorFeaturesEXT }; + VkPhysicalDeviceProvokingVertexFeaturesEXT physicalDeviceProvokingVertexFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, &physicalDevicePrimitiveTopologyListRestartFeaturesEXT }; + VkPhysicalDeviceIndexTypeUint8FeaturesEXT physicalDeviceIndexTypeUint8FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, &physicalDeviceProvokingVertexFeaturesEXT }; + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR physicalDeviceVertexAttributeDivisorFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR, &physicalDeviceIndexTypeUint8FeaturesEXT }; + VkPhysicalDeviceSamplerYcbcrConversionFeatures physicalDeviceSamplerYcbcrConversionFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, &physicalDeviceVertexAttributeDivisorFeaturesKHR }; + VkPhysicalDeviceShaderFloat16Int8Features physicalDeviceShaderFloat16Int8Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, &physicalDeviceSamplerYcbcrConversionFeatures }; + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures physicalDeviceShaderSubgroupExtendedTypesFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, &physicalDeviceShaderFloat16Int8Features }; + VkPhysicalDevice8BitStorageFeatures physicalDevice8BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, &physicalDeviceShaderSubgroupExtendedTypesFeatures }; + VkPhysicalDevice16BitStorageFeatures physicalDevice16BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, &physicalDevice8BitStorageFeatures }; + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT physicalDevicePrimitivesGeneratedQueryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT, &physicalDevice16BitStorageFeatures }; + VkPhysicalDeviceLineRasterizationFeaturesEXT physicalDeviceLineRasterizationFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, &physicalDevicePrimitivesGeneratedQueryFeaturesEXT }; + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG physicalDeviceRelaxedLineRasterizationFeaturesIMG{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG, &physicalDeviceLineRasterizationFeaturesEXT }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceRelaxedLineRasterizationFeaturesIMG)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan11Properties physicalDeviceVulkan11Properties{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceVulkan11Properties)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; +} //namespace MUST +namespace primitivesGeneratedQuery { +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_EXT_PRIMITIVES_GENERATED_QUERY_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT: { + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT* s = static_cast<VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT*>(static_cast<void*>(p)); + s->primitivesGeneratedQuery = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT: { + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT* prettify_VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT = static_cast<VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT->primitivesGeneratedQuery == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT->primitivesGeneratedQuery == VK_TRUE), "Unsupported feature condition: VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT::primitivesGeneratedQuery == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + return ret; + } +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan12Features physicalDeviceVulkan12Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, nullptr }; + VkPhysicalDeviceCustomBorderColorFeaturesEXT physicalDeviceCustomBorderColorFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, &physicalDeviceVulkan12Features }; + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT physicalDevicePrimitiveTopologyListRestartFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, &physicalDeviceCustomBorderColorFeaturesEXT }; + VkPhysicalDeviceProvokingVertexFeaturesEXT physicalDeviceProvokingVertexFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, &physicalDevicePrimitiveTopologyListRestartFeaturesEXT }; + VkPhysicalDeviceIndexTypeUint8FeaturesEXT physicalDeviceIndexTypeUint8FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, &physicalDeviceProvokingVertexFeaturesEXT }; + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR physicalDeviceVertexAttributeDivisorFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR, &physicalDeviceIndexTypeUint8FeaturesEXT }; + VkPhysicalDeviceSamplerYcbcrConversionFeatures physicalDeviceSamplerYcbcrConversionFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, &physicalDeviceVertexAttributeDivisorFeaturesKHR }; + VkPhysicalDeviceShaderFloat16Int8Features physicalDeviceShaderFloat16Int8Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, &physicalDeviceSamplerYcbcrConversionFeatures }; + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures physicalDeviceShaderSubgroupExtendedTypesFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, &physicalDeviceShaderFloat16Int8Features }; + VkPhysicalDevice8BitStorageFeatures physicalDevice8BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, &physicalDeviceShaderSubgroupExtendedTypesFeatures }; + VkPhysicalDevice16BitStorageFeatures physicalDevice16BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, &physicalDevice8BitStorageFeatures }; + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT physicalDevicePrimitivesGeneratedQueryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT, &physicalDevice16BitStorageFeatures }; + VkPhysicalDeviceLineRasterizationFeaturesEXT physicalDeviceLineRasterizationFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, &physicalDevicePrimitivesGeneratedQueryFeaturesEXT }; + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG physicalDeviceRelaxedLineRasterizationFeaturesIMG{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG, &physicalDeviceLineRasterizationFeaturesEXT }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceRelaxedLineRasterizationFeaturesIMG)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan11Properties physicalDeviceVulkan11Properties{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceVulkan11Properties)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; +} //namespace primitivesGeneratedQuery +namespace pipelineStatisticsQuery { +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* s = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + s->features.pipelineStatisticsQuery = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* prettify_VkPhysicalDeviceFeatures2KHR = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.pipelineStatisticsQuery == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.pipelineStatisticsQuery == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.pipelineStatisticsQuery == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + return ret; + } +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan12Features physicalDeviceVulkan12Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, nullptr }; + VkPhysicalDeviceCustomBorderColorFeaturesEXT physicalDeviceCustomBorderColorFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, &physicalDeviceVulkan12Features }; + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT physicalDevicePrimitiveTopologyListRestartFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, &physicalDeviceCustomBorderColorFeaturesEXT }; + VkPhysicalDeviceProvokingVertexFeaturesEXT physicalDeviceProvokingVertexFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, &physicalDevicePrimitiveTopologyListRestartFeaturesEXT }; + VkPhysicalDeviceIndexTypeUint8FeaturesEXT physicalDeviceIndexTypeUint8FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, &physicalDeviceProvokingVertexFeaturesEXT }; + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR physicalDeviceVertexAttributeDivisorFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR, &physicalDeviceIndexTypeUint8FeaturesEXT }; + VkPhysicalDeviceSamplerYcbcrConversionFeatures physicalDeviceSamplerYcbcrConversionFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, &physicalDeviceVertexAttributeDivisorFeaturesKHR }; + VkPhysicalDeviceShaderFloat16Int8Features physicalDeviceShaderFloat16Int8Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, &physicalDeviceSamplerYcbcrConversionFeatures }; + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures physicalDeviceShaderSubgroupExtendedTypesFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, &physicalDeviceShaderFloat16Int8Features }; + VkPhysicalDevice8BitStorageFeatures physicalDevice8BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, &physicalDeviceShaderSubgroupExtendedTypesFeatures }; + VkPhysicalDevice16BitStorageFeatures physicalDevice16BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, &physicalDevice8BitStorageFeatures }; + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT physicalDevicePrimitivesGeneratedQueryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT, &physicalDevice16BitStorageFeatures }; + VkPhysicalDeviceLineRasterizationFeaturesEXT physicalDeviceLineRasterizationFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, &physicalDevicePrimitivesGeneratedQueryFeaturesEXT }; + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG physicalDeviceRelaxedLineRasterizationFeaturesIMG{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG, &physicalDeviceLineRasterizationFeaturesEXT }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceRelaxedLineRasterizationFeaturesIMG)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan11Properties physicalDeviceVulkan11Properties{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceVulkan11Properties)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; +} //namespace pipelineStatisticsQuery +namespace swBresenhamLines { +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT: { + VkPhysicalDeviceLineRasterizationFeaturesEXT* s = static_cast<VkPhysicalDeviceLineRasterizationFeaturesEXT*>(static_cast<void*>(p)); + s->bresenhamLines = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT: { + VkPhysicalDeviceLineRasterizationFeaturesEXT* prettify_VkPhysicalDeviceLineRasterizationFeaturesEXT = static_cast<VkPhysicalDeviceLineRasterizationFeaturesEXT*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceLineRasterizationFeaturesEXT->bresenhamLines == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceLineRasterizationFeaturesEXT->bresenhamLines == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceLineRasterizationFeaturesEXT::bresenhamLines == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + return ret; + } +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan12Features physicalDeviceVulkan12Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, nullptr }; + VkPhysicalDeviceCustomBorderColorFeaturesEXT physicalDeviceCustomBorderColorFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, &physicalDeviceVulkan12Features }; + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT physicalDevicePrimitiveTopologyListRestartFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, &physicalDeviceCustomBorderColorFeaturesEXT }; + VkPhysicalDeviceProvokingVertexFeaturesEXT physicalDeviceProvokingVertexFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, &physicalDevicePrimitiveTopologyListRestartFeaturesEXT }; + VkPhysicalDeviceIndexTypeUint8FeaturesEXT physicalDeviceIndexTypeUint8FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, &physicalDeviceProvokingVertexFeaturesEXT }; + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR physicalDeviceVertexAttributeDivisorFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR, &physicalDeviceIndexTypeUint8FeaturesEXT }; + VkPhysicalDeviceSamplerYcbcrConversionFeatures physicalDeviceSamplerYcbcrConversionFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, &physicalDeviceVertexAttributeDivisorFeaturesKHR }; + VkPhysicalDeviceShaderFloat16Int8Features physicalDeviceShaderFloat16Int8Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, &physicalDeviceSamplerYcbcrConversionFeatures }; + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures physicalDeviceShaderSubgroupExtendedTypesFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, &physicalDeviceShaderFloat16Int8Features }; + VkPhysicalDevice8BitStorageFeatures physicalDevice8BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, &physicalDeviceShaderSubgroupExtendedTypesFeatures }; + VkPhysicalDevice16BitStorageFeatures physicalDevice16BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, &physicalDevice8BitStorageFeatures }; + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT physicalDevicePrimitivesGeneratedQueryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT, &physicalDevice16BitStorageFeatures }; + VkPhysicalDeviceLineRasterizationFeaturesEXT physicalDeviceLineRasterizationFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, &physicalDevicePrimitivesGeneratedQueryFeaturesEXT }; + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG physicalDeviceRelaxedLineRasterizationFeaturesIMG{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG, &physicalDeviceLineRasterizationFeaturesEXT }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceRelaxedLineRasterizationFeaturesIMG)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan11Properties physicalDeviceVulkan11Properties{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceVulkan11Properties)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; +} //namespace swBresenhamLines +namespace hwBresenhamLines { +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_IMG_RELAXED_LINE_RASTERIZATION_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG: { + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG* s = static_cast<VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG*>(static_cast<void*>(p)); + s->relaxedLineRasterization = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG: { + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG* prettify_VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG = static_cast<VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG->relaxedLineRasterization == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG->relaxedLineRasterization == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG::relaxedLineRasterization == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + return ret; + } +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan12Features physicalDeviceVulkan12Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, nullptr }; + VkPhysicalDeviceCustomBorderColorFeaturesEXT physicalDeviceCustomBorderColorFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, &physicalDeviceVulkan12Features }; + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT physicalDevicePrimitiveTopologyListRestartFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, &physicalDeviceCustomBorderColorFeaturesEXT }; + VkPhysicalDeviceProvokingVertexFeaturesEXT physicalDeviceProvokingVertexFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, &physicalDevicePrimitiveTopologyListRestartFeaturesEXT }; + VkPhysicalDeviceIndexTypeUint8FeaturesEXT physicalDeviceIndexTypeUint8FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, &physicalDeviceProvokingVertexFeaturesEXT }; + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR physicalDeviceVertexAttributeDivisorFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR, &physicalDeviceIndexTypeUint8FeaturesEXT }; + VkPhysicalDeviceSamplerYcbcrConversionFeatures physicalDeviceSamplerYcbcrConversionFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, &physicalDeviceVertexAttributeDivisorFeaturesKHR }; + VkPhysicalDeviceShaderFloat16Int8Features physicalDeviceShaderFloat16Int8Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, &physicalDeviceSamplerYcbcrConversionFeatures }; + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures physicalDeviceShaderSubgroupExtendedTypesFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, &physicalDeviceShaderFloat16Int8Features }; + VkPhysicalDevice8BitStorageFeatures physicalDevice8BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, &physicalDeviceShaderSubgroupExtendedTypesFeatures }; + VkPhysicalDevice16BitStorageFeatures physicalDevice16BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, &physicalDevice8BitStorageFeatures }; + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT physicalDevicePrimitivesGeneratedQueryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT, &physicalDevice16BitStorageFeatures }; + VkPhysicalDeviceLineRasterizationFeaturesEXT physicalDeviceLineRasterizationFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, &physicalDevicePrimitivesGeneratedQueryFeaturesEXT }; + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG physicalDeviceRelaxedLineRasterizationFeaturesIMG{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG, &physicalDeviceLineRasterizationFeaturesEXT }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceRelaxedLineRasterizationFeaturesIMG)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceVulkan11Properties physicalDeviceVulkan11Properties{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceVulkan11Properties)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; +} //namespace hwBresenhamLines +} // namespace VP_ANDROID_15_MINIMUMS +#endif // VP_ANDROID_15_minimums + +#ifdef VP_ANDROID_baseline_2021 +namespace VP_ANDROID_BASELINE_2021 { + +static const VkStructureType featureStructTypes[] = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, +}; + +static const VkStructureType propertyStructTypes[] = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, +}; + +static const VkStructureType formatStructTypes[] = { + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, +}; + +static const VkExtensionProperties instanceExtensions[] = { + VkExtensionProperties{ VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SURFACE_EXTENSION_NAME, 1 }, +}; + +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_MAINTENANCE_1_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SWAPCHAIN_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* s = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + s->features.depthBiasClamp = VK_TRUE; + s->features.fragmentStoresAndAtomics = VK_TRUE; + s->features.fullDrawIndexUint32 = VK_TRUE; + s->features.imageCubeArray = VK_TRUE; + s->features.independentBlend = VK_TRUE; + s->features.robustBufferAccess = VK_TRUE; + s->features.sampleRateShading = VK_TRUE; + s->features.shaderSampledImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderStorageImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderUniformBufferArrayDynamicIndexing = VK_TRUE; + s->features.textureCompressionASTC_LDR = VK_TRUE; + s->features.textureCompressionETC2 = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* prettify_VkPhysicalDeviceFeatures2KHR = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.depthBiasClamp == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fragmentStoresAndAtomics == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fullDrawIndexUint32 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.imageCubeArray == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.independentBlend == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.robustBufferAccess == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.sampleRateShading == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderSampledImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionASTC_LDR == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionETC2 == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + return ret; + } +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(nullptr)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(nullptr)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; + +namespace baseline { +static const VkExtensionProperties instanceExtensions[] = { + VkExtensionProperties{ VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SURFACE_EXTENSION_NAME, 1 }, +}; + +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_MAINTENANCE_1_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SWAPCHAIN_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* s = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + s->features.depthBiasClamp = VK_TRUE; + s->features.fragmentStoresAndAtomics = VK_TRUE; + s->features.fullDrawIndexUint32 = VK_TRUE; + s->features.imageCubeArray = VK_TRUE; + s->features.independentBlend = VK_TRUE; + s->features.robustBufferAccess = VK_TRUE; + s->features.sampleRateShading = VK_TRUE; + s->features.shaderSampledImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderStorageImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderUniformBufferArrayDynamicIndexing = VK_TRUE; + s->features.textureCompressionASTC_LDR = VK_TRUE; + s->features.textureCompressionETC2 = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* prettify_VkPhysicalDeviceFeatures2KHR = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.depthBiasClamp == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fragmentStoresAndAtomics == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fullDrawIndexUint32 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.imageCubeArray == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.independentBlend == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.robustBufferAccess == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.sampleRateShading == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderSampledImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionASTC_LDR == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionETC2 == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR: { + VkPhysicalDeviceProperties2KHR* s = static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p)); + s->properties.limits.discreteQueuePriorities = 2; + s->properties.limits.framebufferColorSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferDepthSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferNoAttachmentsSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferStencilSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.maxBoundDescriptorSets = 4; + s->properties.limits.maxColorAttachments = 4; + s->properties.limits.maxComputeSharedMemorySize = 16384; + s->properties.limits.maxComputeWorkGroupCount[0] = 65535; + s->properties.limits.maxComputeWorkGroupCount[1] = 65535; + s->properties.limits.maxComputeWorkGroupCount[2] = 65535; + s->properties.limits.maxComputeWorkGroupInvocations = 128; + s->properties.limits.maxComputeWorkGroupSize[0] = 128; + s->properties.limits.maxComputeWorkGroupSize[1] = 128; + s->properties.limits.maxComputeWorkGroupSize[2] = 64; + s->properties.limits.maxDescriptorSetInputAttachments = 4; + s->properties.limits.maxDescriptorSetSampledImages = 48; + s->properties.limits.maxDescriptorSetSamplers = 48; + s->properties.limits.maxDescriptorSetStorageBuffers = 24; + s->properties.limits.maxDescriptorSetStorageBuffersDynamic = 4; + s->properties.limits.maxDescriptorSetStorageImages = 12; + s->properties.limits.maxDescriptorSetUniformBuffers = 36; + s->properties.limits.maxDescriptorSetUniformBuffersDynamic = 8; + s->properties.limits.maxDrawIndexedIndexValue = 4294967295; + s->properties.limits.maxDrawIndirectCount = 1; + s->properties.limits.maxFragmentCombinedOutputResources = 8; + s->properties.limits.maxFragmentInputComponents = 64; + s->properties.limits.maxFragmentOutputAttachments = 4; + s->properties.limits.maxFramebufferHeight = 4096; + s->properties.limits.maxFramebufferLayers = 256; + s->properties.limits.maxFramebufferWidth = 4096; + s->properties.limits.maxImageArrayLayers = 256; + s->properties.limits.maxImageDimension1D = 4096; + s->properties.limits.maxImageDimension2D = 4096; + s->properties.limits.maxImageDimension3D = 512; + s->properties.limits.maxImageDimensionCube = 4096; + s->properties.limits.maxInterpolationOffset = 0.4375f; + s->properties.limits.maxMemoryAllocationCount = 4096; + s->properties.limits.maxPerStageDescriptorInputAttachments = 4; + s->properties.limits.maxPerStageDescriptorSampledImages = 16; + s->properties.limits.maxPerStageDescriptorSamplers = 16; + s->properties.limits.maxPerStageDescriptorStorageBuffers = 4; + s->properties.limits.maxPerStageDescriptorStorageImages = 4; + s->properties.limits.maxPerStageDescriptorUniformBuffers = 12; + s->properties.limits.maxPerStageResources = 44; + s->properties.limits.maxPushConstantsSize = 128; + s->properties.limits.maxSampleMaskWords = 1; + s->properties.limits.maxSamplerAllocationCount = 4000; + s->properties.limits.maxSamplerAnisotropy = 1.0f; + s->properties.limits.maxSamplerLodBias = 2.0f; + s->properties.limits.maxStorageBufferRange = 134217728; + s->properties.limits.maxTexelBufferElements = 65536; + s->properties.limits.maxTexelOffset = 7; + s->properties.limits.maxUniformBufferRange = 16384; + s->properties.limits.maxVertexInputAttributeOffset = 2047; + s->properties.limits.maxVertexInputAttributes = 16; + s->properties.limits.maxVertexInputBindingStride = 2048; + s->properties.limits.maxVertexInputBindings = 16; + s->properties.limits.maxVertexOutputComponents = 64; + s->properties.limits.maxViewportDimensions[0] = 4096; + s->properties.limits.maxViewportDimensions[1] = 4096; + s->properties.limits.maxViewports = 1; + s->properties.limits.minInterpolationOffset = -0.5f; + s->properties.limits.minMemoryMapAlignment = 4096; + s->properties.limits.minStorageBufferOffsetAlignment = 256; + s->properties.limits.minTexelBufferOffsetAlignment = 256; + s->properties.limits.minTexelOffset = -8; + s->properties.limits.minUniformBufferOffsetAlignment = 256; + s->properties.limits.mipmapPrecisionBits = 4; + s->properties.limits.pointSizeGranularity = 1; + s->properties.limits.sampledImageColorSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.sampledImageDepthSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.sampledImageIntegerSampleCounts |= (VK_SAMPLE_COUNT_1_BIT); + s->properties.limits.sampledImageStencilSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.standardSampleLocations = VK_TRUE; + s->properties.limits.storageImageSampleCounts |= (VK_SAMPLE_COUNT_1_BIT); + s->properties.limits.subPixelInterpolationOffsetBits = 4; + s->properties.limits.subPixelPrecisionBits = 4; + s->properties.limits.subTexelPrecisionBits = 4; + s->properties.limits.viewportBoundsRange[0] = -8192; + s->properties.limits.viewportBoundsRange[1] = 8191; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR: { + VkPhysicalDeviceProperties2KHR* prettify_VkPhysicalDeviceProperties2KHR = static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.discreteQueuePriorities >= 2); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.discreteQueuePriorities >= 2), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.discreteQueuePriorities >= 2"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferColorSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferDepthSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferNoAttachmentsSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferNoAttachmentsSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferNoAttachmentsSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferStencilSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxBoundDescriptorSets >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxBoundDescriptorSets >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxBoundDescriptorSets >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxColorAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxColorAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxColorAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeSharedMemorySize >= 16384); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeSharedMemorySize >= 16384), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeSharedMemorySize >= 16384"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[0] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[0] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[0] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[1] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[1] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[1] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[2] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[2] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[2] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupInvocations >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupInvocations >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupInvocations >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[0] >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[0] >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[0] >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[1] >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[1] >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[1] >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[2] >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[2] >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[2] >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetInputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetInputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetInputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSampledImages >= 48); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSampledImages >= 48), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetSampledImages >= 48"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSamplers >= 48); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSamplers >= 48), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetSamplers >= 48"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffers >= 24); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffers >= 24), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageBuffers >= 24"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageImages >= 12); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageImages >= 12), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageImages >= 12"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffers >= 36); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffers >= 36), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetUniformBuffers >= 36"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndexedIndexValue >= 4294967295); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndexedIndexValue >= 4294967295), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDrawIndexedIndexValue >= 4294967295"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndirectCount >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndirectCount >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDrawIndirectCount >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentCombinedOutputResources >= 8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentCombinedOutputResources >= 8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentCombinedOutputResources >= 8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentInputComponents >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentInputComponents >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentInputComponents >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentOutputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentOutputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentOutputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferHeight >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferHeight >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferHeight >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferLayers >= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferLayers >= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferLayers >= 256"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferWidth >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferWidth >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferWidth >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageArrayLayers >= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageArrayLayers >= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageArrayLayers >= 256"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension1D >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension1D >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension1D >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension2D >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension2D >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension2D >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension3D >= 512); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension3D >= 512), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension3D >= 512"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimensionCube >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimensionCube >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimensionCube >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxInterpolationOffset >= 0.4375); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxInterpolationOffset >= 0.4375), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxInterpolationOffset >= 0.4375"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxMemoryAllocationCount >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxMemoryAllocationCount >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxMemoryAllocationCount >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorInputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorInputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorInputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSampledImages >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSampledImages >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorSampledImages >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSamplers >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSamplers >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorSamplers >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageBuffers >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageBuffers >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorStorageBuffers >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageImages >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageImages >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorStorageImages >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorUniformBuffers >= 12); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorUniformBuffers >= 12), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorUniformBuffers >= 12"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageResources >= 44); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageResources >= 44), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageResources >= 44"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPushConstantsSize >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPushConstantsSize >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPushConstantsSize >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSampleMaskWords >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSampleMaskWords >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSampleMaskWords >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAllocationCount >= 4000); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAllocationCount >= 4000), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerAllocationCount >= 4000"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAnisotropy >= 1.0); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAnisotropy >= 1.0), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerAnisotropy >= 1.0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerLodBias >= 2.0); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerLodBias >= 2.0), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerLodBias >= 2.0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxStorageBufferRange >= 134217728); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxStorageBufferRange >= 134217728), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxStorageBufferRange >= 134217728"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelBufferElements >= 65536); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelBufferElements >= 65536), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxTexelBufferElements >= 65536"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelOffset >= 7); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelOffset >= 7), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxTexelOffset >= 7"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxUniformBufferRange >= 16384); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxUniformBufferRange >= 16384), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxUniformBufferRange >= 16384"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributeOffset >= 2047); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributeOffset >= 2047), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputAttributeOffset >= 2047"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributes >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributes >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputAttributes >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindingStride >= 2048); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindingStride >= 2048), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputBindingStride >= 2048"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindings >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindings >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputBindings >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexOutputComponents >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexOutputComponents >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexOutputComponents >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[0] >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[0] >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewportDimensions[0] >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[1] >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[1] >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewportDimensions[1] >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewports >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewports >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewports >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minInterpolationOffset <= -0.5); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minInterpolationOffset <= -0.5), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minInterpolationOffset <= -0.5"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment <= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment <= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minMemoryMapAlignment <= 4096"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minStorageBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minTexelBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelOffset <= -8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelOffset <= -8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minTexelOffset <= -8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minUniformBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.mipmapPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.mipmapPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.mipmapPrecisionBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity <= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity <= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.pointSizeGranularity <= 1"); + ret = ret && (isMultiple(1, prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity)); VP_DEBUG_COND_MSG(!(isMultiple(1, prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity)), "Unsupported properties condition: isMultiple(1, prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageColorSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageDepthSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageIntegerSampleCounts, (VK_SAMPLE_COUNT_1_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageIntegerSampleCounts, (VK_SAMPLE_COUNT_1_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageIntegerSampleCounts contains (VK_SAMPLE_COUNT_1_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageStencilSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.standardSampleLocations == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.standardSampleLocations == VK_TRUE), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.standardSampleLocations == VK_TRUE"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.storageImageSampleCounts, (VK_SAMPLE_COUNT_1_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.storageImageSampleCounts, (VK_SAMPLE_COUNT_1_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.storageImageSampleCounts contains (VK_SAMPLE_COUNT_1_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelInterpolationOffsetBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelInterpolationOffsetBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subPixelInterpolationOffsetBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subPixelPrecisionBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subTexelPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subTexelPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subTexelPrecisionBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[0] <= -8192); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[0] <= -8192), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.viewportBoundsRange[0] <= -8192"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[1] >= 8191); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[1] >= 8191), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.viewportBoundsRange[1] >= 8191"); + } break; + default: break; + } + return ret; + } +}; + +static const VpFormatDesc formatDesc[] = { + { + VK_FORMAT_A1R5G5B5_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A1R5G5B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A1R5G5B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A2B10G10R10_UINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A2B10G10R10_UNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SRGB_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SRGB_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SRGB_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_UINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_UNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x10_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x10_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x12_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x4_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B10G11R11_UFLOAT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B4G4R4A4_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B4G4R4A4_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B4G4R4A4_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B8G8R8A8_SRGB, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_SRGB: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_SRGB: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B8G8R8A8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_D16_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_D16_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_D32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_D32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11G11_SNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11_SNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R5G6B5_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R5G6B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R5G6B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SRGB, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SRGB: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SRGB: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(nullptr)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(nullptr)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; +} //namespace baseline +} // namespace VP_ANDROID_BASELINE_2021 +#endif // VP_ANDROID_baseline_2021 + +#ifdef VP_ANDROID_baseline_2021_cpu_only +namespace VP_ANDROID_BASELINE_2021_CPU_ONLY { + +static const VkStructureType featureStructTypes[] = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, +}; + +static const VkStructureType propertyStructTypes[] = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, +}; + +static const VkStructureType formatStructTypes[] = { + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, +}; + +static const VkExtensionProperties instanceExtensions[] = { + VkExtensionProperties{ VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SURFACE_EXTENSION_NAME, 1 }, +}; + +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_MAINTENANCE_1_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SWAPCHAIN_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* s = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + s->features.depthBiasClamp = VK_TRUE; + s->features.fragmentStoresAndAtomics = VK_TRUE; + s->features.fullDrawIndexUint32 = VK_TRUE; + s->features.imageCubeArray = VK_TRUE; + s->features.independentBlend = VK_TRUE; + s->features.robustBufferAccess = VK_TRUE; + s->features.sampleRateShading = VK_TRUE; + s->features.shaderSampledImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderStorageImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderUniformBufferArrayDynamicIndexing = VK_TRUE; + s->features.textureCompressionASTC_LDR = VK_TRUE; + s->features.textureCompressionETC2 = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* prettify_VkPhysicalDeviceFeatures2KHR = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.depthBiasClamp == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fragmentStoresAndAtomics == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fullDrawIndexUint32 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.imageCubeArray == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.independentBlend == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.robustBufferAccess == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.sampleRateShading == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderSampledImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionASTC_LDR == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionETC2 == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + return ret; + } +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(nullptr)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(nullptr)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; + +namespace baseline { +static const VkExtensionProperties instanceExtensions[] = { + VkExtensionProperties{ VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SURFACE_EXTENSION_NAME, 1 }, +}; + +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_MAINTENANCE_1_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SWAPCHAIN_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* s = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + s->features.depthBiasClamp = VK_TRUE; + s->features.fragmentStoresAndAtomics = VK_TRUE; + s->features.fullDrawIndexUint32 = VK_TRUE; + s->features.imageCubeArray = VK_TRUE; + s->features.independentBlend = VK_TRUE; + s->features.robustBufferAccess = VK_TRUE; + s->features.sampleRateShading = VK_TRUE; + s->features.shaderSampledImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderStorageImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderUniformBufferArrayDynamicIndexing = VK_TRUE; + s->features.textureCompressionASTC_LDR = VK_TRUE; + s->features.textureCompressionETC2 = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* prettify_VkPhysicalDeviceFeatures2KHR = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.depthBiasClamp == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fragmentStoresAndAtomics == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fullDrawIndexUint32 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.imageCubeArray == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.independentBlend == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.robustBufferAccess == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.sampleRateShading == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderSampledImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionASTC_LDR == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionETC2 == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR: { + VkPhysicalDeviceProperties2KHR* s = static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p)); + s->properties.limits.discreteQueuePriorities = 2; + s->properties.limits.framebufferColorSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferDepthSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferNoAttachmentsSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferStencilSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.maxBoundDescriptorSets = 4; + s->properties.limits.maxColorAttachments = 4; + s->properties.limits.maxComputeSharedMemorySize = 16384; + s->properties.limits.maxComputeWorkGroupCount[0] = 65535; + s->properties.limits.maxComputeWorkGroupCount[1] = 65535; + s->properties.limits.maxComputeWorkGroupCount[2] = 65535; + s->properties.limits.maxComputeWorkGroupInvocations = 128; + s->properties.limits.maxComputeWorkGroupSize[0] = 128; + s->properties.limits.maxComputeWorkGroupSize[1] = 128; + s->properties.limits.maxComputeWorkGroupSize[2] = 64; + s->properties.limits.maxDescriptorSetInputAttachments = 4; + s->properties.limits.maxDescriptorSetSampledImages = 48; + s->properties.limits.maxDescriptorSetSamplers = 48; + s->properties.limits.maxDescriptorSetStorageBuffers = 24; + s->properties.limits.maxDescriptorSetStorageBuffersDynamic = 4; + s->properties.limits.maxDescriptorSetStorageImages = 12; + s->properties.limits.maxDescriptorSetUniformBuffers = 36; + s->properties.limits.maxDescriptorSetUniformBuffersDynamic = 8; + s->properties.limits.maxDrawIndexedIndexValue = 4294967295; + s->properties.limits.maxDrawIndirectCount = 1; + s->properties.limits.maxFragmentCombinedOutputResources = 8; + s->properties.limits.maxFragmentInputComponents = 64; + s->properties.limits.maxFragmentOutputAttachments = 4; + s->properties.limits.maxFramebufferHeight = 4096; + s->properties.limits.maxFramebufferLayers = 256; + s->properties.limits.maxFramebufferWidth = 4096; + s->properties.limits.maxImageArrayLayers = 256; + s->properties.limits.maxImageDimension1D = 4096; + s->properties.limits.maxImageDimension2D = 4096; + s->properties.limits.maxImageDimension3D = 512; + s->properties.limits.maxImageDimensionCube = 4096; + s->properties.limits.maxInterpolationOffset = 0.4375f; + s->properties.limits.maxMemoryAllocationCount = 4096; + s->properties.limits.maxPerStageDescriptorInputAttachments = 4; + s->properties.limits.maxPerStageDescriptorSampledImages = 16; + s->properties.limits.maxPerStageDescriptorSamplers = 16; + s->properties.limits.maxPerStageDescriptorStorageBuffers = 4; + s->properties.limits.maxPerStageDescriptorStorageImages = 4; + s->properties.limits.maxPerStageDescriptorUniformBuffers = 12; + s->properties.limits.maxPerStageResources = 44; + s->properties.limits.maxPushConstantsSize = 128; + s->properties.limits.maxSampleMaskWords = 1; + s->properties.limits.maxSamplerAllocationCount = 4000; + s->properties.limits.maxSamplerAnisotropy = 1.0f; + s->properties.limits.maxSamplerLodBias = 2.0f; + s->properties.limits.maxStorageBufferRange = 134217728; + s->properties.limits.maxTexelBufferElements = 65536; + s->properties.limits.maxTexelOffset = 7; + s->properties.limits.maxUniformBufferRange = 16384; + s->properties.limits.maxVertexInputAttributeOffset = 2047; + s->properties.limits.maxVertexInputAttributes = 16; + s->properties.limits.maxVertexInputBindingStride = 2048; + s->properties.limits.maxVertexInputBindings = 16; + s->properties.limits.maxVertexOutputComponents = 64; + s->properties.limits.maxViewportDimensions[0] = 4096; + s->properties.limits.maxViewportDimensions[1] = 4096; + s->properties.limits.maxViewports = 1; + s->properties.limits.minInterpolationOffset = -0.5f; + s->properties.limits.minMemoryMapAlignment = 4096; + s->properties.limits.minStorageBufferOffsetAlignment = 256; + s->properties.limits.minTexelBufferOffsetAlignment = 256; + s->properties.limits.minTexelOffset = -8; + s->properties.limits.minUniformBufferOffsetAlignment = 256; + s->properties.limits.mipmapPrecisionBits = 4; + s->properties.limits.sampledImageColorSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.sampledImageDepthSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.sampledImageIntegerSampleCounts |= (VK_SAMPLE_COUNT_1_BIT); + s->properties.limits.sampledImageStencilSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.standardSampleLocations = VK_TRUE; + s->properties.limits.storageImageSampleCounts |= (VK_SAMPLE_COUNT_1_BIT); + s->properties.limits.subPixelInterpolationOffsetBits = 4; + s->properties.limits.subPixelPrecisionBits = 4; + s->properties.limits.subTexelPrecisionBits = 4; + s->properties.limits.viewportBoundsRange[0] = -8192; + s->properties.limits.viewportBoundsRange[1] = 8191; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR: { + VkPhysicalDeviceProperties2KHR* prettify_VkPhysicalDeviceProperties2KHR = static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.discreteQueuePriorities >= 2); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.discreteQueuePriorities >= 2), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.discreteQueuePriorities >= 2"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferColorSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferDepthSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferNoAttachmentsSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferNoAttachmentsSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferNoAttachmentsSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferStencilSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxBoundDescriptorSets >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxBoundDescriptorSets >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxBoundDescriptorSets >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxColorAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxColorAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxColorAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeSharedMemorySize >= 16384); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeSharedMemorySize >= 16384), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeSharedMemorySize >= 16384"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[0] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[0] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[0] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[1] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[1] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[1] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[2] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[2] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[2] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupInvocations >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupInvocations >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupInvocations >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[0] >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[0] >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[0] >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[1] >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[1] >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[1] >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[2] >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[2] >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[2] >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetInputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetInputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetInputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSampledImages >= 48); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSampledImages >= 48), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetSampledImages >= 48"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSamplers >= 48); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSamplers >= 48), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetSamplers >= 48"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffers >= 24); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffers >= 24), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageBuffers >= 24"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageImages >= 12); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageImages >= 12), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageImages >= 12"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffers >= 36); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffers >= 36), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetUniformBuffers >= 36"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndexedIndexValue >= 4294967295); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndexedIndexValue >= 4294967295), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDrawIndexedIndexValue >= 4294967295"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndirectCount >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndirectCount >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDrawIndirectCount >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentCombinedOutputResources >= 8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentCombinedOutputResources >= 8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentCombinedOutputResources >= 8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentInputComponents >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentInputComponents >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentInputComponents >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentOutputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentOutputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentOutputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferHeight >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferHeight >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferHeight >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferLayers >= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferLayers >= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferLayers >= 256"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferWidth >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferWidth >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferWidth >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageArrayLayers >= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageArrayLayers >= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageArrayLayers >= 256"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension1D >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension1D >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension1D >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension2D >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension2D >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension2D >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension3D >= 512); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension3D >= 512), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension3D >= 512"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimensionCube >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimensionCube >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimensionCube >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxInterpolationOffset >= 0.4375); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxInterpolationOffset >= 0.4375), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxInterpolationOffset >= 0.4375"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxMemoryAllocationCount >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxMemoryAllocationCount >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxMemoryAllocationCount >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorInputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorInputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorInputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSampledImages >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSampledImages >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorSampledImages >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSamplers >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSamplers >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorSamplers >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageBuffers >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageBuffers >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorStorageBuffers >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageImages >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageImages >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorStorageImages >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorUniformBuffers >= 12); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorUniformBuffers >= 12), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorUniformBuffers >= 12"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageResources >= 44); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageResources >= 44), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageResources >= 44"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPushConstantsSize >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPushConstantsSize >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPushConstantsSize >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSampleMaskWords >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSampleMaskWords >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSampleMaskWords >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAllocationCount >= 4000); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAllocationCount >= 4000), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerAllocationCount >= 4000"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAnisotropy >= 1.0); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAnisotropy >= 1.0), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerAnisotropy >= 1.0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerLodBias >= 2.0); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerLodBias >= 2.0), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerLodBias >= 2.0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxStorageBufferRange >= 134217728); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxStorageBufferRange >= 134217728), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxStorageBufferRange >= 134217728"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelBufferElements >= 65536); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelBufferElements >= 65536), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxTexelBufferElements >= 65536"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelOffset >= 7); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelOffset >= 7), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxTexelOffset >= 7"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxUniformBufferRange >= 16384); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxUniformBufferRange >= 16384), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxUniformBufferRange >= 16384"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributeOffset >= 2047); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributeOffset >= 2047), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputAttributeOffset >= 2047"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributes >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributes >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputAttributes >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindingStride >= 2048); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindingStride >= 2048), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputBindingStride >= 2048"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindings >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindings >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputBindings >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexOutputComponents >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexOutputComponents >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexOutputComponents >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[0] >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[0] >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewportDimensions[0] >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[1] >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[1] >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewportDimensions[1] >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewports >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewports >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewports >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minInterpolationOffset <= -0.5); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minInterpolationOffset <= -0.5), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minInterpolationOffset <= -0.5"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment <= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment <= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minMemoryMapAlignment <= 4096"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minStorageBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minTexelBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelOffset <= -8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelOffset <= -8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minTexelOffset <= -8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minUniformBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.mipmapPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.mipmapPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.mipmapPrecisionBits >= 4"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageColorSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageDepthSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageIntegerSampleCounts, (VK_SAMPLE_COUNT_1_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageIntegerSampleCounts, (VK_SAMPLE_COUNT_1_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageIntegerSampleCounts contains (VK_SAMPLE_COUNT_1_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageStencilSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.standardSampleLocations == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.standardSampleLocations == VK_TRUE), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.standardSampleLocations == VK_TRUE"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.storageImageSampleCounts, (VK_SAMPLE_COUNT_1_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.storageImageSampleCounts, (VK_SAMPLE_COUNT_1_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.storageImageSampleCounts contains (VK_SAMPLE_COUNT_1_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelInterpolationOffsetBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelInterpolationOffsetBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subPixelInterpolationOffsetBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subPixelPrecisionBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subTexelPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subTexelPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subTexelPrecisionBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[0] <= -8192); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[0] <= -8192), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.viewportBoundsRange[0] <= -8192"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[1] >= 8191); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[1] >= 8191), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.viewportBoundsRange[1] >= 8191"); + } break; + default: break; + } + return ret; + } +}; + +static const VpFormatDesc formatDesc[] = { + { + VK_FORMAT_A1R5G5B5_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A1R5G5B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A1R5G5B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A2B10G10R10_UINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A2B10G10R10_UNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SRGB_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SRGB_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SRGB_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_UINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_UNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x10_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x10_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x12_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x4_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B10G11R11_UFLOAT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B4G4R4A4_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B4G4R4A4_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B4G4R4A4_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B8G8R8A8_SRGB, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_SRGB: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_SRGB: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B8G8R8A8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_D16_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_D16_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_D32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_D32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11G11_SNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11_SNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R5G6B5_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R5G6B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R5G6B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SRGB, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SRGB: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SRGB: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(nullptr)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(nullptr)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; +} //namespace baseline +} // namespace VP_ANDROID_BASELINE_2021_CPU_ONLY +#endif // VP_ANDROID_baseline_2021_cpu_only + +#ifdef VP_ANDROID_baseline_2022 +namespace VP_ANDROID_BASELINE_2022 { + +static const VkStructureType featureStructTypes[] = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, +}; + +static const VkStructureType propertyStructTypes[] = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, +}; + +static const VkStructureType formatStructTypes[] = { + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, +}; + +static const VkExtensionProperties instanceExtensions[] = { + VkExtensionProperties{ VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SURFACE_EXTENSION_NAME, 1 }, +}; + +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_MAINTENANCE_1_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SWAPCHAIN_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* s = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + s->features.depthBiasClamp = VK_TRUE; + s->features.fragmentStoresAndAtomics = VK_TRUE; + s->features.fullDrawIndexUint32 = VK_TRUE; + s->features.imageCubeArray = VK_TRUE; + s->features.independentBlend = VK_TRUE; + s->features.largePoints = VK_TRUE; + s->features.robustBufferAccess = VK_TRUE; + s->features.sampleRateShading = VK_TRUE; + s->features.shaderInt16 = VK_TRUE; + s->features.shaderSampledImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderStorageBufferArrayDynamicIndexing = VK_TRUE; + s->features.shaderStorageImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderUniformBufferArrayDynamicIndexing = VK_TRUE; + s->features.textureCompressionASTC_LDR = VK_TRUE; + s->features.textureCompressionETC2 = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: { + VkPhysicalDeviceMultiviewFeatures* s = static_cast<VkPhysicalDeviceMultiviewFeatures*>(static_cast<void*>(p)); + s->multiview = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { + VkPhysicalDeviceSamplerYcbcrConversionFeatures* s = static_cast<VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(static_cast<void*>(p)); + s->samplerYcbcrConversion = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: { + VkPhysicalDeviceShaderDrawParametersFeatures* s = static_cast<VkPhysicalDeviceShaderDrawParametersFeatures*>(static_cast<void*>(p)); + s->shaderDrawParameters = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES: { + VkPhysicalDeviceVariablePointersFeatures* s = static_cast<VkPhysicalDeviceVariablePointersFeatures*>(static_cast<void*>(p)); + s->variablePointers = VK_TRUE; + s->variablePointersStorageBuffer = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* prettify_VkPhysicalDeviceFeatures2KHR = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.depthBiasClamp == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fragmentStoresAndAtomics == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fullDrawIndexUint32 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.imageCubeArray == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.independentBlend == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.largePoints == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.largePoints == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.largePoints == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.robustBufferAccess == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.sampleRateShading == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderInt16 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderInt16 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderInt16 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderSampledImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageBufferArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageBufferArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageBufferArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionASTC_LDR == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionETC2 == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: { + VkPhysicalDeviceMultiviewFeatures* prettify_VkPhysicalDeviceMultiviewFeatures = static_cast<VkPhysicalDeviceMultiviewFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceMultiviewFeatures->multiview == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceMultiviewFeatures->multiview == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceMultiviewFeatures::multiview == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { + VkPhysicalDeviceSamplerYcbcrConversionFeatures* prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures = static_cast<VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures->samplerYcbcrConversion == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures->samplerYcbcrConversion == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: { + VkPhysicalDeviceShaderDrawParametersFeatures* prettify_VkPhysicalDeviceShaderDrawParametersFeatures = static_cast<VkPhysicalDeviceShaderDrawParametersFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceShaderDrawParametersFeatures->shaderDrawParameters == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceShaderDrawParametersFeatures->shaderDrawParameters == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceShaderDrawParametersFeatures::shaderDrawParameters == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES: { + VkPhysicalDeviceVariablePointersFeatures* prettify_VkPhysicalDeviceVariablePointersFeatures = static_cast<VkPhysicalDeviceVariablePointersFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceVariablePointersFeatures->variablePointers == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceVariablePointersFeatures->variablePointers == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceVariablePointersFeatures::variablePointers == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceVariablePointersFeatures->variablePointersStorageBuffer == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceVariablePointersFeatures->variablePointersStorageBuffer == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceVariablePointersFeatures::variablePointersStorageBuffer == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + return ret; + } +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceMultiviewFeatures physicalDeviceMultiviewFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, nullptr }; + VkPhysicalDeviceSamplerYcbcrConversionFeatures physicalDeviceSamplerYcbcrConversionFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, &physicalDeviceMultiviewFeatures }; + VkPhysicalDeviceShaderDrawParametersFeatures physicalDeviceShaderDrawParametersFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, &physicalDeviceSamplerYcbcrConversionFeatures }; + VkPhysicalDeviceVariablePointersFeatures physicalDeviceVariablePointersFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, &physicalDeviceShaderDrawParametersFeatures }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceVariablePointersFeatures)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceMultiviewProperties physicalDeviceMultiviewProperties{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceMultiviewProperties)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; + +namespace baseline { +static const VkExtensionProperties instanceExtensions[] = { + VkExtensionProperties{ VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SURFACE_EXTENSION_NAME, 1 }, +}; + +static const VkExtensionProperties deviceExtensions[] = { + VkExtensionProperties{ VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_MAINTENANCE_1_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_SWAPCHAIN_EXTENSION_NAME, 1 }, + VkExtensionProperties{ VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME, 1 }, +}; + +static const VpFeatureDesc featureDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* s = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + s->features.depthBiasClamp = VK_TRUE; + s->features.fragmentStoresAndAtomics = VK_TRUE; + s->features.fullDrawIndexUint32 = VK_TRUE; + s->features.imageCubeArray = VK_TRUE; + s->features.independentBlend = VK_TRUE; + s->features.largePoints = VK_TRUE; + s->features.robustBufferAccess = VK_TRUE; + s->features.sampleRateShading = VK_TRUE; + s->features.shaderInt16 = VK_TRUE; + s->features.shaderSampledImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderStorageBufferArrayDynamicIndexing = VK_TRUE; + s->features.shaderStorageImageArrayDynamicIndexing = VK_TRUE; + s->features.shaderUniformBufferArrayDynamicIndexing = VK_TRUE; + s->features.textureCompressionASTC_LDR = VK_TRUE; + s->features.textureCompressionETC2 = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: { + VkPhysicalDeviceMultiviewFeatures* s = static_cast<VkPhysicalDeviceMultiviewFeatures*>(static_cast<void*>(p)); + s->multiview = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { + VkPhysicalDeviceSamplerYcbcrConversionFeatures* s = static_cast<VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(static_cast<void*>(p)); + s->samplerYcbcrConversion = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: { + VkPhysicalDeviceShaderDrawParametersFeatures* s = static_cast<VkPhysicalDeviceShaderDrawParametersFeatures*>(static_cast<void*>(p)); + s->shaderDrawParameters = VK_TRUE; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES: { + VkPhysicalDeviceVariablePointersFeatures* s = static_cast<VkPhysicalDeviceVariablePointersFeatures*>(static_cast<void*>(p)); + s->variablePointers = VK_TRUE; + s->variablePointersStorageBuffer = VK_TRUE; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR: { + VkPhysicalDeviceFeatures2KHR* prettify_VkPhysicalDeviceFeatures2KHR = static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.depthBiasClamp == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.depthBiasClamp == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fragmentStoresAndAtomics == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fragmentStoresAndAtomics == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.fullDrawIndexUint32 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.fullDrawIndexUint32 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.imageCubeArray == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.imageCubeArray == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.independentBlend == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.independentBlend == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.largePoints == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.largePoints == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.largePoints == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.robustBufferAccess == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.robustBufferAccess == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.sampleRateShading == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.sampleRateShading == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderInt16 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderInt16 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderInt16 == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderSampledImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderSampledImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageBufferArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageBufferArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageBufferArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderStorageImageArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderStorageImageArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionASTC_LDR == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionASTC_LDR == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceFeatures2KHR->features.textureCompressionETC2 == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceFeatures2KHR::features.textureCompressionETC2 == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: { + VkPhysicalDeviceMultiviewFeatures* prettify_VkPhysicalDeviceMultiviewFeatures = static_cast<VkPhysicalDeviceMultiviewFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceMultiviewFeatures->multiview == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceMultiviewFeatures->multiview == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceMultiviewFeatures::multiview == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { + VkPhysicalDeviceSamplerYcbcrConversionFeatures* prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures = static_cast<VkPhysicalDeviceSamplerYcbcrConversionFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures->samplerYcbcrConversion == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceSamplerYcbcrConversionFeatures->samplerYcbcrConversion == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: { + VkPhysicalDeviceShaderDrawParametersFeatures* prettify_VkPhysicalDeviceShaderDrawParametersFeatures = static_cast<VkPhysicalDeviceShaderDrawParametersFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceShaderDrawParametersFeatures->shaderDrawParameters == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceShaderDrawParametersFeatures->shaderDrawParameters == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceShaderDrawParametersFeatures::shaderDrawParameters == VK_TRUE"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES: { + VkPhysicalDeviceVariablePointersFeatures* prettify_VkPhysicalDeviceVariablePointersFeatures = static_cast<VkPhysicalDeviceVariablePointersFeatures*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceVariablePointersFeatures->variablePointers == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceVariablePointersFeatures->variablePointers == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceVariablePointersFeatures::variablePointers == VK_TRUE"); + ret = ret && (prettify_VkPhysicalDeviceVariablePointersFeatures->variablePointersStorageBuffer == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceVariablePointersFeatures->variablePointersStorageBuffer == VK_TRUE), "Unsupported feature condition: VkPhysicalDeviceVariablePointersFeatures::variablePointersStorageBuffer == VK_TRUE"); + } break; + default: break; + } + return ret; + } +}; + +static const VpPropertyDesc propertyDesc = { + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR: { + VkPhysicalDeviceProperties2KHR* s = static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p)); + s->properties.limits.discreteQueuePriorities = 2; + s->properties.limits.framebufferColorSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferDepthSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferNoAttachmentsSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.framebufferStencilSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.maxBoundDescriptorSets = 4; + s->properties.limits.maxColorAttachments = 4; + s->properties.limits.maxComputeSharedMemorySize = 16384; + s->properties.limits.maxComputeWorkGroupCount[0] = 65535; + s->properties.limits.maxComputeWorkGroupCount[1] = 65535; + s->properties.limits.maxComputeWorkGroupCount[2] = 65535; + s->properties.limits.maxComputeWorkGroupInvocations = 128; + s->properties.limits.maxComputeWorkGroupSize[0] = 128; + s->properties.limits.maxComputeWorkGroupSize[1] = 128; + s->properties.limits.maxComputeWorkGroupSize[2] = 64; + s->properties.limits.maxDescriptorSetInputAttachments = 4; + s->properties.limits.maxDescriptorSetSampledImages = 48; + s->properties.limits.maxDescriptorSetSamplers = 48; + s->properties.limits.maxDescriptorSetStorageBuffers = 24; + s->properties.limits.maxDescriptorSetStorageBuffersDynamic = 4; + s->properties.limits.maxDescriptorSetStorageImages = 12; + s->properties.limits.maxDescriptorSetUniformBuffers = 36; + s->properties.limits.maxDescriptorSetUniformBuffersDynamic = 8; + s->properties.limits.maxDrawIndexedIndexValue = 4294967295; + s->properties.limits.maxDrawIndirectCount = 1; + s->properties.limits.maxFragmentCombinedOutputResources = 8; + s->properties.limits.maxFragmentInputComponents = 64; + s->properties.limits.maxFragmentOutputAttachments = 4; + s->properties.limits.maxFramebufferHeight = 4096; + s->properties.limits.maxFramebufferLayers = 256; + s->properties.limits.maxFramebufferWidth = 4096; + s->properties.limits.maxImageArrayLayers = 256; + s->properties.limits.maxImageDimension1D = 4096; + s->properties.limits.maxImageDimension2D = 4096; + s->properties.limits.maxImageDimension3D = 512; + s->properties.limits.maxImageDimensionCube = 4096; + s->properties.limits.maxInterpolationOffset = 0.4375f; + s->properties.limits.maxMemoryAllocationCount = 4096; + s->properties.limits.maxPerStageDescriptorInputAttachments = 4; + s->properties.limits.maxPerStageDescriptorSampledImages = 16; + s->properties.limits.maxPerStageDescriptorSamplers = 16; + s->properties.limits.maxPerStageDescriptorStorageBuffers = 4; + s->properties.limits.maxPerStageDescriptorStorageImages = 4; + s->properties.limits.maxPerStageDescriptorUniformBuffers = 12; + s->properties.limits.maxPerStageResources = 44; + s->properties.limits.maxPushConstantsSize = 128; + s->properties.limits.maxSampleMaskWords = 1; + s->properties.limits.maxSamplerAllocationCount = 4000; + s->properties.limits.maxSamplerAnisotropy = 1.0f; + s->properties.limits.maxSamplerLodBias = 2.0f; + s->properties.limits.maxStorageBufferRange = 134217728; + s->properties.limits.maxTexelBufferElements = 65536; + s->properties.limits.maxTexelOffset = 7; + s->properties.limits.maxUniformBufferRange = 16384; + s->properties.limits.maxVertexInputAttributeOffset = 2047; + s->properties.limits.maxVertexInputAttributes = 16; + s->properties.limits.maxVertexInputBindingStride = 2048; + s->properties.limits.maxVertexInputBindings = 16; + s->properties.limits.maxVertexOutputComponents = 64; + s->properties.limits.maxViewportDimensions[0] = 4096; + s->properties.limits.maxViewportDimensions[1] = 4096; + s->properties.limits.maxViewports = 1; + s->properties.limits.minInterpolationOffset = -0.5f; + s->properties.limits.minMemoryMapAlignment = 4096; + s->properties.limits.minStorageBufferOffsetAlignment = 256; + s->properties.limits.minTexelBufferOffsetAlignment = 256; + s->properties.limits.minTexelOffset = -8; + s->properties.limits.minUniformBufferOffsetAlignment = 256; + s->properties.limits.mipmapPrecisionBits = 4; + s->properties.limits.pointSizeGranularity = 1; + s->properties.limits.pointSizeRange[0] = 1.0f; + s->properties.limits.pointSizeRange[1] = 511; + s->properties.limits.sampledImageColorSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.sampledImageDepthSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.sampledImageIntegerSampleCounts |= (VK_SAMPLE_COUNT_1_BIT); + s->properties.limits.sampledImageStencilSampleCounts |= (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT); + s->properties.limits.standardSampleLocations = VK_TRUE; + s->properties.limits.storageImageSampleCounts |= (VK_SAMPLE_COUNT_1_BIT); + s->properties.limits.subPixelInterpolationOffsetBits = 4; + s->properties.limits.subPixelPrecisionBits = 4; + s->properties.limits.subTexelPrecisionBits = 4; + s->properties.limits.viewportBoundsRange[0] = -8192; + s->properties.limits.viewportBoundsRange[1] = 8191; + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES: { + VkPhysicalDeviceMultiviewProperties* s = static_cast<VkPhysicalDeviceMultiviewProperties*>(static_cast<void*>(p)); + s->maxMultiviewInstanceIndex = 134217727; + s->maxMultiviewViewCount = 6; + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR: { + VkPhysicalDeviceProperties2KHR* prettify_VkPhysicalDeviceProperties2KHR = static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.discreteQueuePriorities >= 2); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.discreteQueuePriorities >= 2), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.discreteQueuePriorities >= 2"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferColorSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferDepthSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferNoAttachmentsSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferNoAttachmentsSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferNoAttachmentsSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.framebufferStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.framebufferStencilSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxBoundDescriptorSets >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxBoundDescriptorSets >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxBoundDescriptorSets >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxColorAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxColorAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxColorAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeSharedMemorySize >= 16384); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeSharedMemorySize >= 16384), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeSharedMemorySize >= 16384"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[0] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[0] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[0] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[1] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[1] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[1] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[2] >= 65535); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupCount[2] >= 65535), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupCount[2] >= 65535"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupInvocations >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupInvocations >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupInvocations >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[0] >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[0] >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[0] >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[1] >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[1] >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[1] >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[2] >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxComputeWorkGroupSize[2] >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxComputeWorkGroupSize[2] >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetInputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetInputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetInputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSampledImages >= 48); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSampledImages >= 48), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetSampledImages >= 48"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSamplers >= 48); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetSamplers >= 48), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetSamplers >= 48"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffers >= 24); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffers >= 24), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageBuffers >= 24"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageBuffersDynamic >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageImages >= 12); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetStorageImages >= 12), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetStorageImages >= 12"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffers >= 36); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffers >= 36), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetUniformBuffers >= 36"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDescriptorSetUniformBuffersDynamic >= 8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndexedIndexValue >= 4294967295); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndexedIndexValue >= 4294967295), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDrawIndexedIndexValue >= 4294967295"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndirectCount >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxDrawIndirectCount >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxDrawIndirectCount >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentCombinedOutputResources >= 8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentCombinedOutputResources >= 8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentCombinedOutputResources >= 8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentInputComponents >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentInputComponents >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentInputComponents >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentOutputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFragmentOutputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFragmentOutputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferHeight >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferHeight >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferHeight >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferLayers >= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferLayers >= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferLayers >= 256"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferWidth >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxFramebufferWidth >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxFramebufferWidth >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageArrayLayers >= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageArrayLayers >= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageArrayLayers >= 256"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension1D >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension1D >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension1D >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension2D >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension2D >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension2D >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension3D >= 512); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimension3D >= 512), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimension3D >= 512"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimensionCube >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxImageDimensionCube >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxImageDimensionCube >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxInterpolationOffset >= 0.4375); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxInterpolationOffset >= 0.4375), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxInterpolationOffset >= 0.4375"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxMemoryAllocationCount >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxMemoryAllocationCount >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxMemoryAllocationCount >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorInputAttachments >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorInputAttachments >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorInputAttachments >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSampledImages >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSampledImages >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorSampledImages >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSamplers >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorSamplers >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorSamplers >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageBuffers >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageBuffers >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorStorageBuffers >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageImages >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorStorageImages >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorStorageImages >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorUniformBuffers >= 12); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageDescriptorUniformBuffers >= 12), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageDescriptorUniformBuffers >= 12"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageResources >= 44); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPerStageResources >= 44), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPerStageResources >= 44"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPushConstantsSize >= 128); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxPushConstantsSize >= 128), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxPushConstantsSize >= 128"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSampleMaskWords >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSampleMaskWords >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSampleMaskWords >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAllocationCount >= 4000); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAllocationCount >= 4000), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerAllocationCount >= 4000"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAnisotropy >= 1.0); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerAnisotropy >= 1.0), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerAnisotropy >= 1.0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerLodBias >= 2.0); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxSamplerLodBias >= 2.0), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxSamplerLodBias >= 2.0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxStorageBufferRange >= 134217728); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxStorageBufferRange >= 134217728), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxStorageBufferRange >= 134217728"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelBufferElements >= 65536); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelBufferElements >= 65536), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxTexelBufferElements >= 65536"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelOffset >= 7); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxTexelOffset >= 7), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxTexelOffset >= 7"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxUniformBufferRange >= 16384); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxUniformBufferRange >= 16384), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxUniformBufferRange >= 16384"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributeOffset >= 2047); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributeOffset >= 2047), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputAttributeOffset >= 2047"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributes >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputAttributes >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputAttributes >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindingStride >= 2048); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindingStride >= 2048), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputBindingStride >= 2048"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindings >= 16); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexInputBindings >= 16), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexInputBindings >= 16"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexOutputComponents >= 64); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxVertexOutputComponents >= 64), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxVertexOutputComponents >= 64"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[0] >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[0] >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewportDimensions[0] >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[1] >= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewportDimensions[1] >= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewportDimensions[1] >= 4096"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewports >= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.maxViewports >= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.maxViewports >= 1"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minInterpolationOffset <= -0.5); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minInterpolationOffset <= -0.5), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minInterpolationOffset <= -0.5"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment <= 4096); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment <= 4096), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minMemoryMapAlignment <= 4096"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minMemoryMapAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minStorageBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minStorageBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minTexelBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelOffset <= -8); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minTexelOffset <= -8), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minTexelOffset <= -8"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment <= 256); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment <= 256), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.minUniformBufferOffsetAlignment <= 256"); + ret = ret && ((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0); VP_DEBUG_COND_MSG(!((prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0), "Unsupported properties condition: (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment & (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.minUniformBufferOffsetAlignment - 1)) == 0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.mipmapPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.mipmapPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.mipmapPrecisionBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity <= 1); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity <= 1), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.pointSizeGranularity <= 1"); + ret = ret && (isMultiple(1, prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity)); VP_DEBUG_COND_MSG(!(isMultiple(1, prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity)), "Unsupported properties condition: isMultiple(1, prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeGranularity)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeRange[0] <= 1.0); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeRange[0] <= 1.0), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.pointSizeRange[0] <= 1.0"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeRange[1] >= 511); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.pointSizeRange[1] >= 511), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.pointSizeRange[1] >= 511"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageColorSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageColorSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageDepthSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageDepthSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageIntegerSampleCounts, (VK_SAMPLE_COUNT_1_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageIntegerSampleCounts, (VK_SAMPLE_COUNT_1_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageIntegerSampleCounts contains (VK_SAMPLE_COUNT_1_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.sampledImageStencilSampleCounts, (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.sampledImageStencilSampleCounts contains (VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.standardSampleLocations == VK_TRUE); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.standardSampleLocations == VK_TRUE), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.standardSampleLocations == VK_TRUE"); + ret = ret && (vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.storageImageSampleCounts, (VK_SAMPLE_COUNT_1_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.storageImageSampleCounts, (VK_SAMPLE_COUNT_1_BIT))), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.storageImageSampleCounts contains (VK_SAMPLE_COUNT_1_BIT)"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelInterpolationOffsetBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelInterpolationOffsetBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subPixelInterpolationOffsetBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subPixelPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subPixelPrecisionBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subTexelPrecisionBits >= 4); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.subTexelPrecisionBits >= 4), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.subTexelPrecisionBits >= 4"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[0] <= -8192); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[0] <= -8192), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.viewportBoundsRange[0] <= -8192"); + ret = ret && (prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[1] >= 8191); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceProperties2KHR->properties.limits.viewportBoundsRange[1] >= 8191), "Unsupported properties condition: VkPhysicalDeviceProperties2KHR::properties.limits.viewportBoundsRange[1] >= 8191"); + } break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES: { + VkPhysicalDeviceMultiviewProperties* prettify_VkPhysicalDeviceMultiviewProperties = static_cast<VkPhysicalDeviceMultiviewProperties*>(static_cast<void*>(p)); + ret = ret && (prettify_VkPhysicalDeviceMultiviewProperties->maxMultiviewInstanceIndex >= 134217727); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceMultiviewProperties->maxMultiviewInstanceIndex >= 134217727), "Unsupported properties condition: VkPhysicalDeviceMultiviewProperties::maxMultiviewInstanceIndex >= 134217727"); + ret = ret && (prettify_VkPhysicalDeviceMultiviewProperties->maxMultiviewViewCount >= 6); VP_DEBUG_COND_MSG(!(prettify_VkPhysicalDeviceMultiviewProperties->maxMultiviewViewCount >= 6), "Unsupported properties condition: VkPhysicalDeviceMultiviewProperties::maxMultiviewViewCount >= 6"); + } break; + default: break; + } + return ret; + } +}; + +static const VpFormatDesc formatDesc[] = { + { + VK_FORMAT_A1R5G5B5_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A1R5G5B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A1R5G5B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A2B10G10R10_UINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A2B10G10R10_UNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A2B10G10R10_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_SRGB_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SRGB_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_SRGB_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_UINT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UINT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_A8B8G8R8_UNORM_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_A8B8G8R8_UNORM_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x10_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_10x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x10_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x10_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x12_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_12x12_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_4x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x4_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x4_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_5x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_6x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x5_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x5_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x6_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x6_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ASTC_8x8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B10G11R11_UFLOAT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B10G11R11_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B4G4R4A4_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B4G4R4A4_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B4G4R4A4_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B8G8R8A8_SRGB, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_SRGB: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_SRGB: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_B8G8R8A8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_B8G8R8A8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_D16_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_D16_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_D32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_D32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11G11_SNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11G11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11_SNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_SNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_EAC_R11_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_EAC_R11_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16B16A16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16B16A16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16G16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16G16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R16_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R16_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R16_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32B32A32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32B32A32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32G32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32G32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_SFLOAT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SFLOAT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R32_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R32_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R5G6B5_UNORM_PACK16, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R5G6B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R5G6B5_UNORM_PACK16: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_SRGB, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SRGB: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_SRGB: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8B8A8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8B8A8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8G8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8G8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_SINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_SNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_SNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_UINT, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UINT: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, + { + VK_FORMAT_R8_UNORM, + [](VkBaseOutStructure* p) { + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* s = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + s->formatProperties.bufferFeatures |= (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + s->formatProperties.linearTilingFeatures |= (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + s->formatProperties.optimalTilingFeatures |= (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT); + } break; + default: break; + } + }, + [](VkBaseOutStructure* p) -> bool { + bool ret = true; + switch (p->sType) { + case VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR: { + VkFormatProperties2KHR* prettify_VkFormatProperties2KHR = static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p)); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.bufferFeatures, (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.bufferFeatures contains (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.linearTilingFeatures, (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.linearTilingFeatures contains (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + ret = ret && (vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))); VP_DEBUG_COND_MSG(!(vpCheckFlags(prettify_VkFormatProperties2KHR->formatProperties.optimalTilingFeatures, (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT))), "Unsupported format condition for VK_FORMAT_R8_UNORM: VkFormatProperties2KHR::formatProperties.optimalTilingFeatures contains (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)"); + } break; + default: break; + } + return ret; + } + }, +}; + +static const VpStructChainerDesc chainerDesc = { + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceMultiviewFeatures physicalDeviceMultiviewFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, nullptr }; + VkPhysicalDeviceSamplerYcbcrConversionFeatures physicalDeviceSamplerYcbcrConversionFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, &physicalDeviceMultiviewFeatures }; + VkPhysicalDeviceShaderDrawParametersFeatures physicalDeviceShaderDrawParametersFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, &physicalDeviceSamplerYcbcrConversionFeatures }; + VkPhysicalDeviceVariablePointersFeatures physicalDeviceVariablePointersFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, &physicalDeviceShaderDrawParametersFeatures }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceVariablePointersFeatures)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkPhysicalDeviceMultiviewProperties physicalDeviceMultiviewProperties{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&physicalDeviceMultiviewProperties)); + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + pfnCb(p, pUser); + }, + [](VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb) { + VkFormatProperties3KHR formatProperties3KHR{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR, nullptr }; + p->pNext = static_cast<VkBaseOutStructure*>(static_cast<void*>(&formatProperties3KHR)); + pfnCb(p, pUser); + }, +}; +} //namespace baseline +} // namespace VP_ANDROID_BASELINE_2022 +#endif // VP_ANDROID_baseline_2022 + + +#ifdef VP_ANDROID_15_minimums +namespace VP_ANDROID_15_MINIMUMS { + namespace MUST { + static const VpVariantDesc variants[] = { + { + "MUST", + static_cast<uint32_t>(std::size(MUST::instanceExtensions)), MUST::instanceExtensions, + static_cast<uint32_t>(std::size(MUST::deviceExtensions)), MUST::deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + MUST::featureDesc, + static_cast<uint32_t>(std::size(propertyStructTypes)), propertyStructTypes, + MUST::propertyDesc, + 0, nullptr, + 0, nullptr, + static_cast<uint32_t>(std::size(formatStructTypes)), formatStructTypes, + static_cast<uint32_t>(std::size(MUST::formatDesc)), MUST::formatDesc, + MUST::chainerDesc, + }, + }; + static const uint32_t variantCount = static_cast<uint32_t>(std::size(variants)); + } // namespace MUST + + namespace primitivesGeneratedQuery_pipelineStatisticsQuery_ { + static const VpVariantDesc variants[] = { + { + "primitivesGeneratedQuery", + 0, nullptr, + static_cast<uint32_t>(std::size(primitivesGeneratedQuery::deviceExtensions)), primitivesGeneratedQuery::deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + primitivesGeneratedQuery::featureDesc, + 0, nullptr, + primitivesGeneratedQuery::propertyDesc, + 0, nullptr, + 0, nullptr, + 0, nullptr, + 0, nullptr, + primitivesGeneratedQuery::chainerDesc, + }, + { + "pipelineStatisticsQuery", + 0, nullptr, + 0, nullptr, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + pipelineStatisticsQuery::featureDesc, + 0, nullptr, + pipelineStatisticsQuery::propertyDesc, + 0, nullptr, + 0, nullptr, + 0, nullptr, + 0, nullptr, + pipelineStatisticsQuery::chainerDesc, + }, + }; + static const uint32_t variantCount = static_cast<uint32_t>(std::size(variants)); + } // namespace primitivesGeneratedQuery_pipelineStatisticsQuery_ + + namespace swBresenhamLines_hwBresenhamLines_ { + static const VpVariantDesc variants[] = { + { + "swBresenhamLines", + 0, nullptr, + static_cast<uint32_t>(std::size(swBresenhamLines::deviceExtensions)), swBresenhamLines::deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + swBresenhamLines::featureDesc, + 0, nullptr, + swBresenhamLines::propertyDesc, + 0, nullptr, + 0, nullptr, + 0, nullptr, + 0, nullptr, + swBresenhamLines::chainerDesc, + }, + { + "hwBresenhamLines", + 0, nullptr, + static_cast<uint32_t>(std::size(hwBresenhamLines::deviceExtensions)), hwBresenhamLines::deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + hwBresenhamLines::featureDesc, + 0, nullptr, + hwBresenhamLines::propertyDesc, + 0, nullptr, + 0, nullptr, + 0, nullptr, + 0, nullptr, + hwBresenhamLines::chainerDesc, + }, + }; + static const uint32_t variantCount = static_cast<uint32_t>(std::size(variants)); + } // namespace swBresenhamLines_hwBresenhamLines_ + + static const VpCapabilitiesDesc capabilities[] = { + MUST::variantCount, MUST::variants, + primitivesGeneratedQuery_pipelineStatisticsQuery_::variantCount, primitivesGeneratedQuery_pipelineStatisticsQuery_::variants, + swBresenhamLines_hwBresenhamLines_::variantCount, swBresenhamLines_hwBresenhamLines_::variants, + }; + static const uint32_t capabilityCount = static_cast<uint32_t>(std::size(capabilities)); + + static const VpProfileProperties profiles[] = { + {VP_ANDROID_BASELINE_2022_NAME, VP_ANDROID_BASELINE_2022_SPEC_VERSION}, + }; + static const uint32_t profileCount = static_cast<uint32_t>(std::size(profiles)); +} // namespace VP_ANDROID_15_MINIMUMS +#endif //VP_ANDROID_15_minimums + +#ifdef VP_ANDROID_baseline_2021 +namespace VP_ANDROID_BASELINE_2021 { + static const VpVariantDesc mergedCapabilities[] = { + "MERGED", + static_cast<uint32_t>(std::size(instanceExtensions)), instanceExtensions, + static_cast<uint32_t>(std::size(deviceExtensions)), deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + featureDesc, + 0, nullptr, + propertyDesc, + 0, nullptr, + 0, nullptr, + 0, nullptr, + 0, nullptr, + chainerDesc, + }; + + namespace baseline { + static const VpVariantDesc variants[] = { + { + "baseline", + static_cast<uint32_t>(std::size(baseline::instanceExtensions)), baseline::instanceExtensions, + static_cast<uint32_t>(std::size(baseline::deviceExtensions)), baseline::deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + baseline::featureDesc, + static_cast<uint32_t>(std::size(propertyStructTypes)), propertyStructTypes, + baseline::propertyDesc, + 0, nullptr, + 0, nullptr, + static_cast<uint32_t>(std::size(formatStructTypes)), formatStructTypes, + static_cast<uint32_t>(std::size(baseline::formatDesc)), baseline::formatDesc, + baseline::chainerDesc, + }, + }; + static const uint32_t variantCount = static_cast<uint32_t>(std::size(variants)); + } // namespace baseline + + static const VpCapabilitiesDesc capabilities[] = { + baseline::variantCount, baseline::variants, + }; + static const uint32_t capabilityCount = static_cast<uint32_t>(std::size(capabilities)); +} // namespace VP_ANDROID_BASELINE_2021 +#endif //VP_ANDROID_baseline_2021 + +#ifdef VP_ANDROID_baseline_2021_cpu_only +namespace VP_ANDROID_BASELINE_2021_CPU_ONLY { + static const VpVariantDesc mergedCapabilities[] = { + "MERGED", + static_cast<uint32_t>(std::size(instanceExtensions)), instanceExtensions, + static_cast<uint32_t>(std::size(deviceExtensions)), deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + featureDesc, + 0, nullptr, + propertyDesc, + 0, nullptr, + 0, nullptr, + 0, nullptr, + 0, nullptr, + chainerDesc, + }; + + namespace baseline { + static const VpVariantDesc variants[] = { + { + "baseline", + static_cast<uint32_t>(std::size(baseline::instanceExtensions)), baseline::instanceExtensions, + static_cast<uint32_t>(std::size(baseline::deviceExtensions)), baseline::deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + baseline::featureDesc, + static_cast<uint32_t>(std::size(propertyStructTypes)), propertyStructTypes, + baseline::propertyDesc, + 0, nullptr, + 0, nullptr, + static_cast<uint32_t>(std::size(formatStructTypes)), formatStructTypes, + static_cast<uint32_t>(std::size(baseline::formatDesc)), baseline::formatDesc, + baseline::chainerDesc, + }, + }; + static const uint32_t variantCount = static_cast<uint32_t>(std::size(variants)); + } // namespace baseline + + static const VpCapabilitiesDesc capabilities[] = { + baseline::variantCount, baseline::variants, + }; + static const uint32_t capabilityCount = static_cast<uint32_t>(std::size(capabilities)); +} // namespace VP_ANDROID_BASELINE_2021_CPU_ONLY +#endif //VP_ANDROID_baseline_2021_cpu_only + +#ifdef VP_ANDROID_baseline_2022 +namespace VP_ANDROID_BASELINE_2022 { + static const VpVariantDesc mergedCapabilities[] = { + "MERGED", + static_cast<uint32_t>(std::size(instanceExtensions)), instanceExtensions, + static_cast<uint32_t>(std::size(deviceExtensions)), deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + featureDesc, + 0, nullptr, + propertyDesc, + 0, nullptr, + 0, nullptr, + 0, nullptr, + 0, nullptr, + chainerDesc, + }; + + namespace baseline { + static const VpVariantDesc variants[] = { + { + "baseline", + static_cast<uint32_t>(std::size(baseline::instanceExtensions)), baseline::instanceExtensions, + static_cast<uint32_t>(std::size(baseline::deviceExtensions)), baseline::deviceExtensions, + static_cast<uint32_t>(std::size(featureStructTypes)), featureStructTypes, + baseline::featureDesc, + static_cast<uint32_t>(std::size(propertyStructTypes)), propertyStructTypes, + baseline::propertyDesc, + 0, nullptr, + 0, nullptr, + static_cast<uint32_t>(std::size(formatStructTypes)), formatStructTypes, + static_cast<uint32_t>(std::size(baseline::formatDesc)), baseline::formatDesc, + baseline::chainerDesc, + }, + }; + static const uint32_t variantCount = static_cast<uint32_t>(std::size(variants)); + } // namespace baseline + + static const VpCapabilitiesDesc capabilities[] = { + baseline::variantCount, baseline::variants, + }; + static const uint32_t capabilityCount = static_cast<uint32_t>(std::size(capabilities)); +} // namespace VP_ANDROID_BASELINE_2022 +#endif //VP_ANDROID_baseline_2022 + +static const VpProfileDesc profiles[] = { +#ifdef VP_ANDROID_15_minimums + VpProfileDesc{ + VpProfileProperties{ VP_ANDROID_15_MINIMUMS_NAME, VP_ANDROID_15_MINIMUMS_SPEC_VERSION }, + VP_ANDROID_15_MINIMUMS_MIN_API_VERSION, + nullptr, + VP_ANDROID_15_MINIMUMS::profileCount, VP_ANDROID_15_MINIMUMS::profiles, + VP_ANDROID_15_MINIMUMS::capabilityCount, VP_ANDROID_15_MINIMUMS::capabilities, + 0, nullptr, + }, +#endif // VP_ANDROID_15_MINIMUMS +#ifdef VP_ANDROID_baseline_2021 + VpProfileDesc{ + VpProfileProperties{ VP_ANDROID_BASELINE_2021_NAME, VP_ANDROID_BASELINE_2021_SPEC_VERSION }, + VP_ANDROID_BASELINE_2021_MIN_API_VERSION, + VP_ANDROID_BASELINE_2021::mergedCapabilities, + 0, nullptr, + VP_ANDROID_BASELINE_2021::capabilityCount, VP_ANDROID_BASELINE_2021::capabilities, + 0, nullptr, + }, +#endif // VP_ANDROID_BASELINE_2021 +#ifdef VP_ANDROID_baseline_2021_cpu_only + VpProfileDesc{ + VpProfileProperties{ VP_ANDROID_BASELINE_2021_CPU_ONLY_NAME, VP_ANDROID_BASELINE_2021_CPU_ONLY_SPEC_VERSION }, + VP_ANDROID_BASELINE_2021_CPU_ONLY_MIN_API_VERSION, + VP_ANDROID_BASELINE_2021_CPU_ONLY::mergedCapabilities, + 0, nullptr, + VP_ANDROID_BASELINE_2021_CPU_ONLY::capabilityCount, VP_ANDROID_BASELINE_2021_CPU_ONLY::capabilities, + 0, nullptr, + }, +#endif // VP_ANDROID_BASELINE_2021_CPU_ONLY +#ifdef VP_ANDROID_baseline_2022 + VpProfileDesc{ + VpProfileProperties{ VP_ANDROID_BASELINE_2022_NAME, VP_ANDROID_BASELINE_2022_SPEC_VERSION }, + VP_ANDROID_BASELINE_2022_MIN_API_VERSION, + VP_ANDROID_BASELINE_2022::mergedCapabilities, + 0, nullptr, + VP_ANDROID_BASELINE_2022::capabilityCount, VP_ANDROID_BASELINE_2022::capabilities, + 0, nullptr, + }, +#endif // VP_ANDROID_BASELINE_2022 +}; +static const uint32_t profileCount = static_cast<uint32_t>(std::size(profiles)); + + +struct FeaturesChain { + std::map<VkStructureType, std::size_t> structureSize; + + template<typename T> + constexpr std::size_t size() const { + return (sizeof(T) - sizeof(VkBaseOutStructure)) / sizeof(VkBool32); + } + + // Chain with all Vulkan Features structures + VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV physicalDeviceDeviceGeneratedCommandsFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV, nullptr }; + VkPhysicalDeviceDeviceGeneratedCommandsComputeFeaturesNV physicalDeviceDeviceGeneratedCommandsComputeFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_COMPUTE_FEATURES_NV, nullptr }; + VkPhysicalDevicePrivateDataFeatures physicalDevicePrivateDataFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES, nullptr }; + VkPhysicalDeviceVariablePointersFeatures physicalDeviceVariablePointersFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, nullptr }; + VkPhysicalDeviceMultiviewFeatures physicalDeviceMultiviewFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, nullptr }; + VkPhysicalDevicePresentIdFeaturesKHR physicalDevicePresentIdFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR, nullptr }; + VkPhysicalDevicePresentWaitFeaturesKHR physicalDevicePresentWaitFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR, nullptr }; + VkPhysicalDevice16BitStorageFeatures physicalDevice16BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, nullptr }; + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures physicalDeviceShaderSubgroupExtendedTypesFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, nullptr }; + VkPhysicalDeviceSamplerYcbcrConversionFeatures physicalDeviceSamplerYcbcrConversionFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, nullptr }; + VkPhysicalDeviceProtectedMemoryFeatures physicalDeviceProtectedMemoryFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES, nullptr }; + VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT physicalDeviceBlendOperationAdvancedFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT, nullptr }; + VkPhysicalDeviceMultiDrawFeaturesEXT physicalDeviceMultiDrawFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT, nullptr }; + VkPhysicalDeviceInlineUniformBlockFeatures physicalDeviceInlineUniformBlockFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES, nullptr }; + VkPhysicalDeviceMaintenance4Features physicalDeviceMaintenance4Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES, nullptr }; + VkPhysicalDeviceMaintenance5FeaturesKHR physicalDeviceMaintenance5FeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES_KHR, nullptr }; + VkPhysicalDeviceMaintenance6FeaturesKHR physicalDeviceMaintenance6FeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_6_FEATURES_KHR, nullptr }; + VkPhysicalDeviceShaderDrawParametersFeatures physicalDeviceShaderDrawParametersFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, nullptr }; + VkPhysicalDeviceShaderFloat16Int8Features physicalDeviceShaderFloat16Int8Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, nullptr }; + VkPhysicalDeviceHostQueryResetFeatures physicalDeviceHostQueryResetFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, nullptr }; + VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR physicalDeviceGlobalPriorityQueryFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR, nullptr }; + VkPhysicalDeviceDeviceMemoryReportFeaturesEXT physicalDeviceDeviceMemoryReportFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_MEMORY_REPORT_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDescriptorIndexingFeatures physicalDeviceDescriptorIndexingFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, nullptr }; + VkPhysicalDeviceTimelineSemaphoreFeatures physicalDeviceTimelineSemaphoreFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, nullptr }; + VkPhysicalDevice8BitStorageFeatures physicalDevice8BitStorageFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, nullptr }; + VkPhysicalDeviceConditionalRenderingFeaturesEXT physicalDeviceConditionalRenderingFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT, nullptr }; + VkPhysicalDeviceVulkanMemoryModelFeatures physicalDeviceVulkanMemoryModelFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES, nullptr }; + VkPhysicalDeviceShaderAtomicInt64Features physicalDeviceShaderAtomicInt64Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, nullptr }; + VkPhysicalDeviceShaderAtomicFloatFeaturesEXT physicalDeviceShaderAtomicFloatFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT, nullptr }; + VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT physicalDeviceShaderAtomicFloat2FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT, nullptr }; + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR physicalDeviceVertexAttributeDivisorFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR, nullptr }; + VkPhysicalDeviceASTCDecodeFeaturesEXT physicalDeviceASTCDecodeFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceTransformFeedbackFeaturesEXT physicalDeviceTransformFeedbackFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT, nullptr }; + VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV physicalDeviceRepresentativeFragmentTestFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV, nullptr }; + VkPhysicalDeviceExclusiveScissorFeaturesNV physicalDeviceExclusiveScissorFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV, nullptr }; + VkPhysicalDeviceCornerSampledImageFeaturesNV physicalDeviceCornerSampledImageFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV, nullptr }; + VkPhysicalDeviceComputeShaderDerivativesFeaturesNV physicalDeviceComputeShaderDerivativesFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_NV, nullptr }; + VkPhysicalDeviceShaderImageFootprintFeaturesNV physicalDeviceShaderImageFootprintFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_FOOTPRINT_FEATURES_NV, nullptr }; + VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV physicalDeviceDedicatedAllocationImageAliasingFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEDICATED_ALLOCATION_IMAGE_ALIASING_FEATURES_NV, nullptr }; + VkPhysicalDeviceCopyMemoryIndirectFeaturesNV physicalDeviceCopyMemoryIndirectFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COPY_MEMORY_INDIRECT_FEATURES_NV, nullptr }; + VkPhysicalDeviceMemoryDecompressionFeaturesNV physicalDeviceMemoryDecompressionFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_DECOMPRESSION_FEATURES_NV, nullptr }; + VkPhysicalDeviceShadingRateImageFeaturesNV physicalDeviceShadingRateImageFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV, nullptr }; + VkPhysicalDeviceInvocationMaskFeaturesHUAWEI physicalDeviceInvocationMaskFeaturesHUAWEI{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INVOCATION_MASK_FEATURES_HUAWEI, nullptr }; + VkPhysicalDeviceMeshShaderFeaturesNV physicalDeviceMeshShaderFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV, nullptr }; + VkPhysicalDeviceMeshShaderFeaturesEXT physicalDeviceMeshShaderFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT, nullptr }; + VkPhysicalDeviceAccelerationStructureFeaturesKHR physicalDeviceAccelerationStructureFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR, nullptr }; + VkPhysicalDeviceRayTracingPipelineFeaturesKHR physicalDeviceRayTracingPipelineFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR, nullptr }; + VkPhysicalDeviceRayQueryFeaturesKHR physicalDeviceRayQueryFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR, nullptr }; + VkPhysicalDeviceRayTracingMaintenance1FeaturesKHR physicalDeviceRayTracingMaintenance1FeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MAINTENANCE_1_FEATURES_KHR, nullptr }; + VkPhysicalDeviceFragmentDensityMapFeaturesEXT physicalDeviceFragmentDensityMapFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT, nullptr }; + VkPhysicalDeviceFragmentDensityMap2FeaturesEXT physicalDeviceFragmentDensityMap2FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT, nullptr }; + VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM physicalDeviceFragmentDensityMapOffsetFeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceScalarBlockLayoutFeatures physicalDeviceScalarBlockLayoutFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, nullptr }; + VkPhysicalDeviceUniformBufferStandardLayoutFeatures physicalDeviceUniformBufferStandardLayoutFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, nullptr }; + VkPhysicalDeviceDepthClipEnableFeaturesEXT physicalDeviceDepthClipEnableFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceMemoryPriorityFeaturesEXT physicalDeviceMemoryPriorityFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT, nullptr }; + VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT physicalDevicePageableDeviceLocalMemoryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT, nullptr }; + VkPhysicalDeviceBufferDeviceAddressFeatures physicalDeviceBufferDeviceAddressFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, nullptr }; + VkPhysicalDeviceBufferDeviceAddressFeaturesEXT physicalDeviceBufferDeviceAddressFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT, nullptr }; + VkPhysicalDeviceImagelessFramebufferFeatures physicalDeviceImagelessFramebufferFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES, nullptr }; + VkPhysicalDeviceTextureCompressionASTCHDRFeatures physicalDeviceTextureCompressionASTCHDRFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES, nullptr }; + VkPhysicalDeviceCooperativeMatrixFeaturesNV physicalDeviceCooperativeMatrixFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_NV, nullptr }; + VkPhysicalDeviceYcbcrImageArraysFeaturesEXT physicalDeviceYcbcrImageArraysFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_IMAGE_ARRAYS_FEATURES_EXT, nullptr }; + VkPhysicalDevicePresentBarrierFeaturesNV physicalDevicePresentBarrierFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_BARRIER_FEATURES_NV, nullptr }; + VkPhysicalDevicePerformanceQueryFeaturesKHR physicalDevicePerformanceQueryFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR, nullptr }; + VkPhysicalDeviceCoverageReductionModeFeaturesNV physicalDeviceCoverageReductionModeFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COVERAGE_REDUCTION_MODE_FEATURES_NV, nullptr }; + VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL physicalDeviceShaderIntegerFunctions2FeaturesINTEL{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL, nullptr }; + VkPhysicalDeviceShaderClockFeaturesKHR physicalDeviceShaderClockFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CLOCK_FEATURES_KHR, nullptr }; + VkPhysicalDeviceIndexTypeUint8FeaturesEXT physicalDeviceIndexTypeUint8FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, nullptr }; + VkPhysicalDeviceShaderSMBuiltinsFeaturesNV physicalDeviceShaderSMBuiltinsFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV, nullptr }; + VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT physicalDeviceFragmentShaderInterlockFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT, nullptr }; + VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures physicalDeviceSeparateDepthStencilLayoutsFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES, nullptr }; + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT physicalDevicePrimitiveTopologyListRestartFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, nullptr }; + VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR physicalDevicePipelineExecutablePropertiesFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR, nullptr }; + VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures physicalDeviceShaderDemoteToHelperInvocationFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES, nullptr }; + VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT physicalDeviceTexelBufferAlignmentFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT, nullptr }; + VkPhysicalDeviceSubgroupSizeControlFeatures physicalDeviceSubgroupSizeControlFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES, nullptr }; + VkPhysicalDeviceLineRasterizationFeaturesEXT physicalDeviceLineRasterizationFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, nullptr }; + VkPhysicalDevicePipelineCreationCacheControlFeatures physicalDevicePipelineCreationCacheControlFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES, nullptr }; + VkPhysicalDeviceVulkan11Features physicalDeviceVulkan11Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, nullptr }; + VkPhysicalDeviceVulkan12Features physicalDeviceVulkan12Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, nullptr }; + VkPhysicalDeviceVulkan13Features physicalDeviceVulkan13Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, nullptr }; + VkPhysicalDeviceCoherentMemoryFeaturesAMD physicalDeviceCoherentMemoryFeaturesAMD{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD, nullptr }; + VkPhysicalDeviceCustomBorderColorFeaturesEXT physicalDeviceCustomBorderColorFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, nullptr }; + VkPhysicalDeviceBorderColorSwizzleFeaturesEXT physicalDeviceBorderColorSwizzleFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceExtendedDynamicStateFeaturesEXT physicalDeviceExtendedDynamicStateFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceExtendedDynamicState2FeaturesEXT physicalDeviceExtendedDynamicState2FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT, nullptr }; + VkPhysicalDeviceExtendedDynamicState3FeaturesEXT physicalDeviceExtendedDynamicState3FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDiagnosticsConfigFeaturesNV physicalDeviceDiagnosticsConfigFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV, nullptr }; + VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures physicalDeviceZeroInitializeWorkgroupMemoryFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES, nullptr }; + VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR physicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR, nullptr }; + VkPhysicalDeviceRobustness2FeaturesEXT physicalDeviceRobustness2FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, nullptr }; + VkPhysicalDeviceImageRobustnessFeatures physicalDeviceImageRobustnessFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES, nullptr }; + VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR physicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR, nullptr }; +#ifdef VK_ENABLE_BETA_EXTENSIONS + VkPhysicalDevicePortabilitySubsetFeaturesKHR physicalDevicePortabilitySubsetFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR, nullptr }; +#endif + VkPhysicalDevice4444FormatsFeaturesEXT physicalDevice4444FormatsFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT, nullptr }; + VkPhysicalDeviceSubpassShadingFeaturesHUAWEI physicalDeviceSubpassShadingFeaturesHUAWEI{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_FEATURES_HUAWEI, nullptr }; + VkPhysicalDeviceClusterCullingShaderFeaturesHUAWEI physicalDeviceClusterCullingShaderFeaturesHUAWEI{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CLUSTER_CULLING_SHADER_FEATURES_HUAWEI, nullptr }; + VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT physicalDeviceShaderImageAtomicInt64FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT, nullptr }; + VkPhysicalDeviceFragmentShadingRateFeaturesKHR physicalDeviceFragmentShadingRateFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, nullptr }; + VkPhysicalDeviceShaderTerminateInvocationFeatures physicalDeviceShaderTerminateInvocationFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES, nullptr }; + VkPhysicalDeviceFragmentShadingRateEnumsFeaturesNV physicalDeviceFragmentShadingRateEnumsFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_FEATURES_NV, nullptr }; + VkPhysicalDeviceImage2DViewOf3DFeaturesEXT physicalDeviceImage2DViewOf3DFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT, nullptr }; + VkPhysicalDeviceImageSlicedViewOf3DFeaturesEXT physicalDeviceImageSlicedViewOf3DFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_SLICED_VIEW_OF_3D_FEATURES_EXT, nullptr }; + VkPhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT physicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT physicalDeviceMutableDescriptorTypeFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDepthClipControlFeaturesEXT physicalDeviceDepthClipControlFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT, nullptr }; + VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT physicalDeviceVertexInputDynamicStateFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceExternalMemoryRDMAFeaturesNV physicalDeviceExternalMemoryRDMAFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_RDMA_FEATURES_NV, nullptr }; + VkPhysicalDeviceColorWriteEnableFeaturesEXT physicalDeviceColorWriteEnableFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceSynchronization2Features physicalDeviceSynchronization2Features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, nullptr }; + VkPhysicalDeviceHostImageCopyFeaturesEXT physicalDeviceHostImageCopyFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES_EXT, nullptr }; + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT physicalDevicePrimitivesGeneratedQueryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT, nullptr }; + VkPhysicalDeviceLegacyDitheringFeaturesEXT physicalDeviceLegacyDitheringFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LEGACY_DITHERING_FEATURES_EXT, nullptr }; + VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT physicalDeviceMultisampledRenderToSingleSampledFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT, nullptr }; + VkPhysicalDevicePipelineProtectedAccessFeaturesEXT physicalDevicePipelineProtectedAccessFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_PROTECTED_ACCESS_FEATURES_EXT, nullptr }; + VkPhysicalDeviceVideoMaintenance1FeaturesKHR physicalDeviceVideoMaintenance1FeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR, nullptr }; + VkPhysicalDeviceInheritedViewportScissorFeaturesNV physicalDeviceInheritedViewportScissorFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INHERITED_VIEWPORT_SCISSOR_FEATURES_NV, nullptr }; + VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT physicalDeviceYcbcr2Plane444FormatsFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT, nullptr }; + VkPhysicalDeviceProvokingVertexFeaturesEXT physicalDeviceProvokingVertexFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDescriptorBufferFeaturesEXT physicalDeviceDescriptorBufferFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT, nullptr }; + VkPhysicalDeviceShaderIntegerDotProductFeatures physicalDeviceShaderIntegerDotProductFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES, nullptr }; + VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR physicalDeviceFragmentShaderBarycentricFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR, nullptr }; + VkPhysicalDeviceRayTracingMotionBlurFeaturesNV physicalDeviceRayTracingMotionBlurFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MOTION_BLUR_FEATURES_NV, nullptr }; + VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT physicalDeviceRGBA10X6FormatsFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDynamicRenderingFeatures physicalDeviceDynamicRenderingFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, nullptr }; + VkPhysicalDeviceImageViewMinLodFeaturesEXT physicalDeviceImageViewMinLodFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT, nullptr }; + VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT physicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT, nullptr }; + VkPhysicalDeviceLinearColorAttachmentFeaturesNV physicalDeviceLinearColorAttachmentFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINEAR_COLOR_ATTACHMENT_FEATURES_NV, nullptr }; + VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT physicalDeviceGraphicsPipelineLibraryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDescriptorSetHostMappingFeaturesVALVE physicalDeviceDescriptorSetHostMappingFeaturesVALVE{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_SET_HOST_MAPPING_FEATURES_VALVE, nullptr }; + VkPhysicalDeviceNestedCommandBufferFeaturesEXT physicalDeviceNestedCommandBufferFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NESTED_COMMAND_BUFFER_FEATURES_EXT, nullptr }; + VkPhysicalDeviceShaderModuleIdentifierFeaturesEXT physicalDeviceShaderModuleIdentifierFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_FEATURES_EXT, nullptr }; + VkPhysicalDeviceImageCompressionControlFeaturesEXT physicalDeviceImageCompressionControlFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT, nullptr }; + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT physicalDeviceImageCompressionControlSwapchainFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT, nullptr }; + VkPhysicalDeviceSubpassMergeFeedbackFeaturesEXT physicalDeviceSubpassMergeFeedbackFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_MERGE_FEEDBACK_FEATURES_EXT, nullptr }; + VkPhysicalDeviceOpacityMicromapFeaturesEXT physicalDeviceOpacityMicromapFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_EXT, nullptr }; +#ifdef VK_ENABLE_BETA_EXTENSIONS + VkPhysicalDeviceDisplacementMicromapFeaturesNV physicalDeviceDisplacementMicromapFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISPLACEMENT_MICROMAP_FEATURES_NV, nullptr }; +#endif + VkPhysicalDevicePipelinePropertiesFeaturesEXT physicalDevicePipelinePropertiesFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_PROPERTIES_FEATURES_EXT, nullptr }; + VkPhysicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD physicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_EARLY_AND_LATE_FRAGMENT_TESTS_FEATURES_AMD, nullptr }; + VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT physicalDeviceNonSeamlessCubeMapFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT, nullptr }; + VkPhysicalDevicePipelineRobustnessFeaturesEXT physicalDevicePipelineRobustnessFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT, nullptr }; + VkPhysicalDeviceImageProcessingFeaturesQCOM physicalDeviceImageProcessingFeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceTilePropertiesFeaturesQCOM physicalDeviceTilePropertiesFeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_PROPERTIES_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceAmigoProfilingFeaturesSEC physicalDeviceAmigoProfilingFeaturesSEC{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_AMIGO_PROFILING_FEATURES_SEC, nullptr }; + VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT physicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDepthClampZeroOneFeaturesEXT physicalDeviceDepthClampZeroOneFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLAMP_ZERO_ONE_FEATURES_EXT, nullptr }; + VkPhysicalDeviceAddressBindingReportFeaturesEXT physicalDeviceAddressBindingReportFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ADDRESS_BINDING_REPORT_FEATURES_EXT, nullptr }; + VkPhysicalDeviceOpticalFlowFeaturesNV physicalDeviceOpticalFlowFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_FEATURES_NV, nullptr }; + VkPhysicalDeviceFaultFeaturesEXT physicalDeviceFaultFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT, nullptr }; + VkPhysicalDevicePipelineLibraryGroupHandlesFeaturesEXT physicalDevicePipelineLibraryGroupHandlesFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_LIBRARY_GROUP_HANDLES_FEATURES_EXT, nullptr }; + VkPhysicalDeviceShaderCoreBuiltinsFeaturesARM physicalDeviceShaderCoreBuiltinsFeaturesARM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_BUILTINS_FEATURES_ARM, nullptr }; + VkPhysicalDeviceFrameBoundaryFeaturesEXT physicalDeviceFrameBoundaryFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAME_BOUNDARY_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT physicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT, nullptr }; + VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT physicalDeviceSwapchainMaintenance1FeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, nullptr }; + VkPhysicalDeviceDepthBiasControlFeaturesEXT physicalDeviceDepthBiasControlFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_BIAS_CONTROL_FEATURES_EXT, nullptr }; + VkPhysicalDeviceRayTracingInvocationReorderFeaturesNV physicalDeviceRayTracingInvocationReorderFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_INVOCATION_REORDER_FEATURES_NV, nullptr }; + VkPhysicalDeviceExtendedSparseAddressSpaceFeaturesNV physicalDeviceExtendedSparseAddressSpaceFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_SPARSE_ADDRESS_SPACE_FEATURES_NV, nullptr }; + VkPhysicalDeviceMultiviewPerViewViewportsFeaturesQCOM physicalDeviceMultiviewPerViewViewportsFeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_VIEWPORTS_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR physicalDeviceRayTracingPositionFetchFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR, nullptr }; + VkPhysicalDeviceMultiviewPerViewRenderAreasFeaturesQCOM physicalDeviceMultiviewPerViewRenderAreasFeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_RENDER_AREAS_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceShaderObjectFeaturesEXT physicalDeviceShaderObjectFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT, nullptr }; + VkPhysicalDeviceShaderTileImageFeaturesEXT physicalDeviceShaderTileImageFeaturesEXT{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TILE_IMAGE_FEATURES_EXT, nullptr }; +#ifdef VK_USE_PLATFORM_SCREEN_QNX + VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX physicalDeviceExternalMemoryScreenBufferFeaturesQNX{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_SCREEN_BUFFER_FEATURES_QNX, nullptr }; +#endif + VkPhysicalDeviceCooperativeMatrixFeaturesKHR physicalDeviceCooperativeMatrixFeaturesKHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR, nullptr }; +#ifdef VK_ENABLE_BETA_EXTENSIONS + VkPhysicalDeviceShaderEnqueueFeaturesAMDX physicalDeviceShaderEnqueueFeaturesAMDX{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ENQUEUE_FEATURES_AMDX, nullptr }; +#endif + VkPhysicalDeviceCubicClampFeaturesQCOM physicalDeviceCubicClampFeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUBIC_CLAMP_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceYcbcrDegammaFeaturesQCOM physicalDeviceYcbcrDegammaFeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_DEGAMMA_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceCubicWeightsFeaturesQCOM physicalDeviceCubicWeightsFeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUBIC_WEIGHTS_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceImageProcessing2FeaturesQCOM physicalDeviceImageProcessing2FeaturesQCOM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_2_FEATURES_QCOM, nullptr }; + VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV physicalDeviceDescriptorPoolOverallocationFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV, nullptr }; + VkPhysicalDevicePerStageDescriptorSetFeaturesNV physicalDevicePerStageDescriptorSetFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PER_STAGE_DESCRIPTOR_SET_FEATURES_NV, nullptr }; +#ifdef VK_USE_PLATFORM_ANDROID_KHR + VkPhysicalDeviceExternalFormatResolveFeaturesANDROID physicalDeviceExternalFormatResolveFeaturesANDROID{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FORMAT_RESOLVE_FEATURES_ANDROID, nullptr }; +#endif + VkPhysicalDeviceCudaKernelLaunchFeaturesNV physicalDeviceCudaKernelLaunchFeaturesNV{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUDA_KERNEL_LAUNCH_FEATURES_NV, nullptr }; + VkPhysicalDeviceSchedulingControlsFeaturesARM physicalDeviceSchedulingControlsFeaturesARM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCHEDULING_CONTROLS_FEATURES_ARM, nullptr }; + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG physicalDeviceRelaxedLineRasterizationFeaturesIMG{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG, nullptr }; + VkPhysicalDeviceRenderPassStripedFeaturesARM physicalDeviceRenderPassStripedFeaturesARM{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RENDER_PASS_STRIPED_FEATURES_ARM, nullptr }; + VkPhysicalDeviceFeatures2KHR physicalDeviceFeatures2KHR{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, nullptr }; + + FeaturesChain() { + // Initializing all feature structures, number of Features (VkBool32) per structure. + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV, size<VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_COMPUTE_FEATURES_NV, size<VkPhysicalDeviceDeviceGeneratedCommandsComputeFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES, size<VkPhysicalDevicePrivateDataFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, size<VkPhysicalDeviceVariablePointersFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, size<VkPhysicalDeviceMultiviewFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR, size<VkPhysicalDevicePresentIdFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR, size<VkPhysicalDevicePresentWaitFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, size<VkPhysicalDevice16BitStorageFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, size<VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, size<VkPhysicalDeviceSamplerYcbcrConversionFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES, size<VkPhysicalDeviceProtectedMemoryFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT, size<VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT, size<VkPhysicalDeviceMultiDrawFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES, size<VkPhysicalDeviceInlineUniformBlockFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES, size<VkPhysicalDeviceMaintenance4Features>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES_KHR, size<VkPhysicalDeviceMaintenance5FeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_6_FEATURES_KHR, size<VkPhysicalDeviceMaintenance6FeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, size<VkPhysicalDeviceShaderDrawParametersFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, size<VkPhysicalDeviceShaderFloat16Int8Features>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, size<VkPhysicalDeviceHostQueryResetFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR, size<VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_MEMORY_REPORT_FEATURES_EXT, size<VkPhysicalDeviceDeviceMemoryReportFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, size<VkPhysicalDeviceDescriptorIndexingFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, size<VkPhysicalDeviceTimelineSemaphoreFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, size<VkPhysicalDevice8BitStorageFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT, size<VkPhysicalDeviceConditionalRenderingFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES, size<VkPhysicalDeviceVulkanMemoryModelFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, size<VkPhysicalDeviceShaderAtomicInt64Features>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT, size<VkPhysicalDeviceShaderAtomicFloatFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT, size<VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_KHR, size<VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT, size<VkPhysicalDeviceASTCDecodeFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT, size<VkPhysicalDeviceTransformFeedbackFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV, size<VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV, size<VkPhysicalDeviceExclusiveScissorFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV, size<VkPhysicalDeviceCornerSampledImageFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_NV, size<VkPhysicalDeviceComputeShaderDerivativesFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_FOOTPRINT_FEATURES_NV, size<VkPhysicalDeviceShaderImageFootprintFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEDICATED_ALLOCATION_IMAGE_ALIASING_FEATURES_NV, size<VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COPY_MEMORY_INDIRECT_FEATURES_NV, size<VkPhysicalDeviceCopyMemoryIndirectFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_DECOMPRESSION_FEATURES_NV, size<VkPhysicalDeviceMemoryDecompressionFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV, size<VkPhysicalDeviceShadingRateImageFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INVOCATION_MASK_FEATURES_HUAWEI, size<VkPhysicalDeviceInvocationMaskFeaturesHUAWEI>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV, size<VkPhysicalDeviceMeshShaderFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT, size<VkPhysicalDeviceMeshShaderFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR, size<VkPhysicalDeviceAccelerationStructureFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR, size<VkPhysicalDeviceRayTracingPipelineFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR, size<VkPhysicalDeviceRayQueryFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MAINTENANCE_1_FEATURES_KHR, size<VkPhysicalDeviceRayTracingMaintenance1FeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT, size<VkPhysicalDeviceFragmentDensityMapFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT, size<VkPhysicalDeviceFragmentDensityMap2FeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_FEATURES_QCOM, size<VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, size<VkPhysicalDeviceScalarBlockLayoutFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, size<VkPhysicalDeviceUniformBufferStandardLayoutFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT, size<VkPhysicalDeviceDepthClipEnableFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT, size<VkPhysicalDeviceMemoryPriorityFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT, size<VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, size<VkPhysicalDeviceBufferDeviceAddressFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT, size<VkPhysicalDeviceBufferDeviceAddressFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES, size<VkPhysicalDeviceImagelessFramebufferFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES, size<VkPhysicalDeviceTextureCompressionASTCHDRFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_NV, size<VkPhysicalDeviceCooperativeMatrixFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_IMAGE_ARRAYS_FEATURES_EXT, size<VkPhysicalDeviceYcbcrImageArraysFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_BARRIER_FEATURES_NV, size<VkPhysicalDevicePresentBarrierFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR, size<VkPhysicalDevicePerformanceQueryFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COVERAGE_REDUCTION_MODE_FEATURES_NV, size<VkPhysicalDeviceCoverageReductionModeFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL, size<VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CLOCK_FEATURES_KHR, size<VkPhysicalDeviceShaderClockFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT, size<VkPhysicalDeviceIndexTypeUint8FeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV, size<VkPhysicalDeviceShaderSMBuiltinsFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT, size<VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES, size<VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT, size<VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR, size<VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES, size<VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT, size<VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES, size<VkPhysicalDeviceSubgroupSizeControlFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, size<VkPhysicalDeviceLineRasterizationFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES, size<VkPhysicalDevicePipelineCreationCacheControlFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, size<VkPhysicalDeviceVulkan11Features>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, size<VkPhysicalDeviceVulkan12Features>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, size<VkPhysicalDeviceVulkan13Features>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD, size<VkPhysicalDeviceCoherentMemoryFeaturesAMD>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, size<VkPhysicalDeviceCustomBorderColorFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT, size<VkPhysicalDeviceBorderColorSwizzleFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT, size<VkPhysicalDeviceExtendedDynamicStateFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT, size<VkPhysicalDeviceExtendedDynamicState2FeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT, size<VkPhysicalDeviceExtendedDynamicState3FeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV, size<VkPhysicalDeviceDiagnosticsConfigFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES, size<VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR, size<VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, size<VkPhysicalDeviceRobustness2FeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES, size<VkPhysicalDeviceImageRobustnessFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR, size<VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR>() }); +#ifdef VK_ENABLE_BETA_EXTENSIONS + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR, size<VkPhysicalDevicePortabilitySubsetFeaturesKHR>() }); +#endif + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT, size<VkPhysicalDevice4444FormatsFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_FEATURES_HUAWEI, size<VkPhysicalDeviceSubpassShadingFeaturesHUAWEI>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CLUSTER_CULLING_SHADER_FEATURES_HUAWEI, size<VkPhysicalDeviceClusterCullingShaderFeaturesHUAWEI>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT, size<VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, size<VkPhysicalDeviceFragmentShadingRateFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES, size<VkPhysicalDeviceShaderTerminateInvocationFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_FEATURES_NV, size<VkPhysicalDeviceFragmentShadingRateEnumsFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT, size<VkPhysicalDeviceImage2DViewOf3DFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_SLICED_VIEW_OF_3D_FEATURES_EXT, size<VkPhysicalDeviceImageSlicedViewOf3DFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_FEATURES_EXT, size<VkPhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT, size<VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT, size<VkPhysicalDeviceDepthClipControlFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT, size<VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_RDMA_FEATURES_NV, size<VkPhysicalDeviceExternalMemoryRDMAFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT, size<VkPhysicalDeviceColorWriteEnableFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, size<VkPhysicalDeviceSynchronization2Features>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES_EXT, size<VkPhysicalDeviceHostImageCopyFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT, size<VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LEGACY_DITHERING_FEATURES_EXT, size<VkPhysicalDeviceLegacyDitheringFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT, size<VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_PROTECTED_ACCESS_FEATURES_EXT, size<VkPhysicalDevicePipelineProtectedAccessFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR, size<VkPhysicalDeviceVideoMaintenance1FeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INHERITED_VIEWPORT_SCISSOR_FEATURES_NV, size<VkPhysicalDeviceInheritedViewportScissorFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT, size<VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT, size<VkPhysicalDeviceProvokingVertexFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT, size<VkPhysicalDeviceDescriptorBufferFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES, size<VkPhysicalDeviceShaderIntegerDotProductFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR, size<VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MOTION_BLUR_FEATURES_NV, size<VkPhysicalDeviceRayTracingMotionBlurFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT, size<VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, size<VkPhysicalDeviceDynamicRenderingFeatures>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT, size<VkPhysicalDeviceImageViewMinLodFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT, size<VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINEAR_COLOR_ATTACHMENT_FEATURES_NV, size<VkPhysicalDeviceLinearColorAttachmentFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT, size<VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_SET_HOST_MAPPING_FEATURES_VALVE, size<VkPhysicalDeviceDescriptorSetHostMappingFeaturesVALVE>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NESTED_COMMAND_BUFFER_FEATURES_EXT, size<VkPhysicalDeviceNestedCommandBufferFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_FEATURES_EXT, size<VkPhysicalDeviceShaderModuleIdentifierFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT, size<VkPhysicalDeviceImageCompressionControlFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT, size<VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_MERGE_FEEDBACK_FEATURES_EXT, size<VkPhysicalDeviceSubpassMergeFeedbackFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_EXT, size<VkPhysicalDeviceOpacityMicromapFeaturesEXT>() }); +#ifdef VK_ENABLE_BETA_EXTENSIONS + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISPLACEMENT_MICROMAP_FEATURES_NV, size<VkPhysicalDeviceDisplacementMicromapFeaturesNV>() }); +#endif + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_PROPERTIES_FEATURES_EXT, size<VkPhysicalDevicePipelinePropertiesFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_EARLY_AND_LATE_FRAGMENT_TESTS_FEATURES_AMD, size<VkPhysicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT, size<VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT, size<VkPhysicalDevicePipelineRobustnessFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM, size<VkPhysicalDeviceImageProcessingFeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_PROPERTIES_FEATURES_QCOM, size<VkPhysicalDeviceTilePropertiesFeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_AMIGO_PROFILING_FEATURES_SEC, size<VkPhysicalDeviceAmigoProfilingFeaturesSEC>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT, size<VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLAMP_ZERO_ONE_FEATURES_EXT, size<VkPhysicalDeviceDepthClampZeroOneFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ADDRESS_BINDING_REPORT_FEATURES_EXT, size<VkPhysicalDeviceAddressBindingReportFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_FEATURES_NV, size<VkPhysicalDeviceOpticalFlowFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT, size<VkPhysicalDeviceFaultFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_LIBRARY_GROUP_HANDLES_FEATURES_EXT, size<VkPhysicalDevicePipelineLibraryGroupHandlesFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_BUILTINS_FEATURES_ARM, size<VkPhysicalDeviceShaderCoreBuiltinsFeaturesARM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAME_BOUNDARY_FEATURES_EXT, size<VkPhysicalDeviceFrameBoundaryFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT, size<VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, size<VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_BIAS_CONTROL_FEATURES_EXT, size<VkPhysicalDeviceDepthBiasControlFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_INVOCATION_REORDER_FEATURES_NV, size<VkPhysicalDeviceRayTracingInvocationReorderFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_SPARSE_ADDRESS_SPACE_FEATURES_NV, size<VkPhysicalDeviceExtendedSparseAddressSpaceFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_VIEWPORTS_FEATURES_QCOM, size<VkPhysicalDeviceMultiviewPerViewViewportsFeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR, size<VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_RENDER_AREAS_FEATURES_QCOM, size<VkPhysicalDeviceMultiviewPerViewRenderAreasFeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT, size<VkPhysicalDeviceShaderObjectFeaturesEXT>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TILE_IMAGE_FEATURES_EXT, size<VkPhysicalDeviceShaderTileImageFeaturesEXT>() }); +#ifdef VK_USE_PLATFORM_SCREEN_QNX + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_SCREEN_BUFFER_FEATURES_QNX, size<VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX>() }); +#endif + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR, size<VkPhysicalDeviceCooperativeMatrixFeaturesKHR>() }); +#ifdef VK_ENABLE_BETA_EXTENSIONS + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ENQUEUE_FEATURES_AMDX, size<VkPhysicalDeviceShaderEnqueueFeaturesAMDX>() }); +#endif + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUBIC_CLAMP_FEATURES_QCOM, size<VkPhysicalDeviceCubicClampFeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_DEGAMMA_FEATURES_QCOM, size<VkPhysicalDeviceYcbcrDegammaFeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUBIC_WEIGHTS_FEATURES_QCOM, size<VkPhysicalDeviceCubicWeightsFeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_2_FEATURES_QCOM, size<VkPhysicalDeviceImageProcessing2FeaturesQCOM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV, size<VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PER_STAGE_DESCRIPTOR_SET_FEATURES_NV, size<VkPhysicalDevicePerStageDescriptorSetFeaturesNV>() }); +#ifdef VK_USE_PLATFORM_ANDROID_KHR + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FORMAT_RESOLVE_FEATURES_ANDROID, size<VkPhysicalDeviceExternalFormatResolveFeaturesANDROID>() }); +#endif + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUDA_KERNEL_LAUNCH_FEATURES_NV, size<VkPhysicalDeviceCudaKernelLaunchFeaturesNV>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCHEDULING_CONTROLS_FEATURES_ARM, size<VkPhysicalDeviceSchedulingControlsFeaturesARM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG, size<VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RENDER_PASS_STRIPED_FEATURES_ARM, size<VkPhysicalDeviceRenderPassStripedFeaturesARM>() }); + this->structureSize.insert({ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, size<VkPhysicalDeviceFeatures2KHR>() }); + + //Initializing the full list of available structure features + void* pNext = nullptr; + physicalDeviceDeviceGeneratedCommandsFeaturesNV.pNext = pNext; + pNext = &physicalDeviceDeviceGeneratedCommandsFeaturesNV; + physicalDeviceDeviceGeneratedCommandsComputeFeaturesNV.pNext = pNext; + pNext = &physicalDeviceDeviceGeneratedCommandsComputeFeaturesNV; + physicalDevicePrivateDataFeatures.pNext = pNext; + pNext = &physicalDevicePrivateDataFeatures; + physicalDeviceVariablePointersFeatures.pNext = pNext; + pNext = &physicalDeviceVariablePointersFeatures; + physicalDeviceMultiviewFeatures.pNext = pNext; + pNext = &physicalDeviceMultiviewFeatures; + physicalDevicePresentIdFeaturesKHR.pNext = pNext; + pNext = &physicalDevicePresentIdFeaturesKHR; + physicalDevicePresentWaitFeaturesKHR.pNext = pNext; + pNext = &physicalDevicePresentWaitFeaturesKHR; + physicalDevice16BitStorageFeatures.pNext = pNext; + pNext = &physicalDevice16BitStorageFeatures; + physicalDeviceShaderSubgroupExtendedTypesFeatures.pNext = pNext; + pNext = &physicalDeviceShaderSubgroupExtendedTypesFeatures; + physicalDeviceSamplerYcbcrConversionFeatures.pNext = pNext; + pNext = &physicalDeviceSamplerYcbcrConversionFeatures; + physicalDeviceProtectedMemoryFeatures.pNext = pNext; + pNext = &physicalDeviceProtectedMemoryFeatures; + physicalDeviceBlendOperationAdvancedFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceBlendOperationAdvancedFeaturesEXT; + physicalDeviceMultiDrawFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceMultiDrawFeaturesEXT; + physicalDeviceInlineUniformBlockFeatures.pNext = pNext; + pNext = &physicalDeviceInlineUniformBlockFeatures; + physicalDeviceMaintenance4Features.pNext = pNext; + pNext = &physicalDeviceMaintenance4Features; + physicalDeviceMaintenance5FeaturesKHR.pNext = pNext; + pNext = &physicalDeviceMaintenance5FeaturesKHR; + physicalDeviceMaintenance6FeaturesKHR.pNext = pNext; + pNext = &physicalDeviceMaintenance6FeaturesKHR; + physicalDeviceShaderDrawParametersFeatures.pNext = pNext; + pNext = &physicalDeviceShaderDrawParametersFeatures; + physicalDeviceShaderFloat16Int8Features.pNext = pNext; + pNext = &physicalDeviceShaderFloat16Int8Features; + physicalDeviceHostQueryResetFeatures.pNext = pNext; + pNext = &physicalDeviceHostQueryResetFeatures; + physicalDeviceGlobalPriorityQueryFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceGlobalPriorityQueryFeaturesKHR; + physicalDeviceDeviceMemoryReportFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceDeviceMemoryReportFeaturesEXT; + physicalDeviceDescriptorIndexingFeatures.pNext = pNext; + pNext = &physicalDeviceDescriptorIndexingFeatures; + physicalDeviceTimelineSemaphoreFeatures.pNext = pNext; + pNext = &physicalDeviceTimelineSemaphoreFeatures; + physicalDevice8BitStorageFeatures.pNext = pNext; + pNext = &physicalDevice8BitStorageFeatures; + physicalDeviceConditionalRenderingFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceConditionalRenderingFeaturesEXT; + physicalDeviceVulkanMemoryModelFeatures.pNext = pNext; + pNext = &physicalDeviceVulkanMemoryModelFeatures; + physicalDeviceShaderAtomicInt64Features.pNext = pNext; + pNext = &physicalDeviceShaderAtomicInt64Features; + physicalDeviceShaderAtomicFloatFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceShaderAtomicFloatFeaturesEXT; + physicalDeviceShaderAtomicFloat2FeaturesEXT.pNext = pNext; + pNext = &physicalDeviceShaderAtomicFloat2FeaturesEXT; + physicalDeviceVertexAttributeDivisorFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceVertexAttributeDivisorFeaturesKHR; + physicalDeviceASTCDecodeFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceASTCDecodeFeaturesEXT; + physicalDeviceTransformFeedbackFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceTransformFeedbackFeaturesEXT; + physicalDeviceRepresentativeFragmentTestFeaturesNV.pNext = pNext; + pNext = &physicalDeviceRepresentativeFragmentTestFeaturesNV; + physicalDeviceExclusiveScissorFeaturesNV.pNext = pNext; + pNext = &physicalDeviceExclusiveScissorFeaturesNV; + physicalDeviceCornerSampledImageFeaturesNV.pNext = pNext; + pNext = &physicalDeviceCornerSampledImageFeaturesNV; + physicalDeviceComputeShaderDerivativesFeaturesNV.pNext = pNext; + pNext = &physicalDeviceComputeShaderDerivativesFeaturesNV; + physicalDeviceShaderImageFootprintFeaturesNV.pNext = pNext; + pNext = &physicalDeviceShaderImageFootprintFeaturesNV; + physicalDeviceDedicatedAllocationImageAliasingFeaturesNV.pNext = pNext; + pNext = &physicalDeviceDedicatedAllocationImageAliasingFeaturesNV; + physicalDeviceCopyMemoryIndirectFeaturesNV.pNext = pNext; + pNext = &physicalDeviceCopyMemoryIndirectFeaturesNV; + physicalDeviceMemoryDecompressionFeaturesNV.pNext = pNext; + pNext = &physicalDeviceMemoryDecompressionFeaturesNV; + physicalDeviceShadingRateImageFeaturesNV.pNext = pNext; + pNext = &physicalDeviceShadingRateImageFeaturesNV; + physicalDeviceInvocationMaskFeaturesHUAWEI.pNext = pNext; + pNext = &physicalDeviceInvocationMaskFeaturesHUAWEI; + physicalDeviceMeshShaderFeaturesNV.pNext = pNext; + pNext = &physicalDeviceMeshShaderFeaturesNV; + physicalDeviceMeshShaderFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceMeshShaderFeaturesEXT; + physicalDeviceAccelerationStructureFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceAccelerationStructureFeaturesKHR; + physicalDeviceRayTracingPipelineFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceRayTracingPipelineFeaturesKHR; + physicalDeviceRayQueryFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceRayQueryFeaturesKHR; + physicalDeviceRayTracingMaintenance1FeaturesKHR.pNext = pNext; + pNext = &physicalDeviceRayTracingMaintenance1FeaturesKHR; + physicalDeviceFragmentDensityMapFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceFragmentDensityMapFeaturesEXT; + physicalDeviceFragmentDensityMap2FeaturesEXT.pNext = pNext; + pNext = &physicalDeviceFragmentDensityMap2FeaturesEXT; + physicalDeviceFragmentDensityMapOffsetFeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceFragmentDensityMapOffsetFeaturesQCOM; + physicalDeviceScalarBlockLayoutFeatures.pNext = pNext; + pNext = &physicalDeviceScalarBlockLayoutFeatures; + physicalDeviceUniformBufferStandardLayoutFeatures.pNext = pNext; + pNext = &physicalDeviceUniformBufferStandardLayoutFeatures; + physicalDeviceDepthClipEnableFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceDepthClipEnableFeaturesEXT; + physicalDeviceMemoryPriorityFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceMemoryPriorityFeaturesEXT; + physicalDevicePageableDeviceLocalMemoryFeaturesEXT.pNext = pNext; + pNext = &physicalDevicePageableDeviceLocalMemoryFeaturesEXT; + physicalDeviceBufferDeviceAddressFeatures.pNext = pNext; + pNext = &physicalDeviceBufferDeviceAddressFeatures; + physicalDeviceBufferDeviceAddressFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceBufferDeviceAddressFeaturesEXT; + physicalDeviceImagelessFramebufferFeatures.pNext = pNext; + pNext = &physicalDeviceImagelessFramebufferFeatures; + physicalDeviceTextureCompressionASTCHDRFeatures.pNext = pNext; + pNext = &physicalDeviceTextureCompressionASTCHDRFeatures; + physicalDeviceCooperativeMatrixFeaturesNV.pNext = pNext; + pNext = &physicalDeviceCooperativeMatrixFeaturesNV; + physicalDeviceYcbcrImageArraysFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceYcbcrImageArraysFeaturesEXT; + physicalDevicePresentBarrierFeaturesNV.pNext = pNext; + pNext = &physicalDevicePresentBarrierFeaturesNV; + physicalDevicePerformanceQueryFeaturesKHR.pNext = pNext; + pNext = &physicalDevicePerformanceQueryFeaturesKHR; + physicalDeviceCoverageReductionModeFeaturesNV.pNext = pNext; + pNext = &physicalDeviceCoverageReductionModeFeaturesNV; + physicalDeviceShaderIntegerFunctions2FeaturesINTEL.pNext = pNext; + pNext = &physicalDeviceShaderIntegerFunctions2FeaturesINTEL; + physicalDeviceShaderClockFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceShaderClockFeaturesKHR; + physicalDeviceIndexTypeUint8FeaturesEXT.pNext = pNext; + pNext = &physicalDeviceIndexTypeUint8FeaturesEXT; + physicalDeviceShaderSMBuiltinsFeaturesNV.pNext = pNext; + pNext = &physicalDeviceShaderSMBuiltinsFeaturesNV; + physicalDeviceFragmentShaderInterlockFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceFragmentShaderInterlockFeaturesEXT; + physicalDeviceSeparateDepthStencilLayoutsFeatures.pNext = pNext; + pNext = &physicalDeviceSeparateDepthStencilLayoutsFeatures; + physicalDevicePrimitiveTopologyListRestartFeaturesEXT.pNext = pNext; + pNext = &physicalDevicePrimitiveTopologyListRestartFeaturesEXT; + physicalDevicePipelineExecutablePropertiesFeaturesKHR.pNext = pNext; + pNext = &physicalDevicePipelineExecutablePropertiesFeaturesKHR; + physicalDeviceShaderDemoteToHelperInvocationFeatures.pNext = pNext; + pNext = &physicalDeviceShaderDemoteToHelperInvocationFeatures; + physicalDeviceTexelBufferAlignmentFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceTexelBufferAlignmentFeaturesEXT; + physicalDeviceSubgroupSizeControlFeatures.pNext = pNext; + pNext = &physicalDeviceSubgroupSizeControlFeatures; + physicalDeviceLineRasterizationFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceLineRasterizationFeaturesEXT; + physicalDevicePipelineCreationCacheControlFeatures.pNext = pNext; + pNext = &physicalDevicePipelineCreationCacheControlFeatures; + physicalDeviceVulkan11Features.pNext = pNext; + pNext = &physicalDeviceVulkan11Features; + physicalDeviceVulkan12Features.pNext = pNext; + pNext = &physicalDeviceVulkan12Features; + physicalDeviceVulkan13Features.pNext = pNext; + pNext = &physicalDeviceVulkan13Features; + physicalDeviceCoherentMemoryFeaturesAMD.pNext = pNext; + pNext = &physicalDeviceCoherentMemoryFeaturesAMD; + physicalDeviceCustomBorderColorFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceCustomBorderColorFeaturesEXT; + physicalDeviceBorderColorSwizzleFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceBorderColorSwizzleFeaturesEXT; + physicalDeviceExtendedDynamicStateFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceExtendedDynamicStateFeaturesEXT; + physicalDeviceExtendedDynamicState2FeaturesEXT.pNext = pNext; + pNext = &physicalDeviceExtendedDynamicState2FeaturesEXT; + physicalDeviceExtendedDynamicState3FeaturesEXT.pNext = pNext; + pNext = &physicalDeviceExtendedDynamicState3FeaturesEXT; + physicalDeviceDiagnosticsConfigFeaturesNV.pNext = pNext; + pNext = &physicalDeviceDiagnosticsConfigFeaturesNV; + physicalDeviceZeroInitializeWorkgroupMemoryFeatures.pNext = pNext; + pNext = &physicalDeviceZeroInitializeWorkgroupMemoryFeatures; + physicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR; + physicalDeviceRobustness2FeaturesEXT.pNext = pNext; + pNext = &physicalDeviceRobustness2FeaturesEXT; + physicalDeviceImageRobustnessFeatures.pNext = pNext; + pNext = &physicalDeviceImageRobustnessFeatures; + physicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR; +#ifdef VK_ENABLE_BETA_EXTENSIONS + physicalDevicePortabilitySubsetFeaturesKHR.pNext = pNext; + pNext = &physicalDevicePortabilitySubsetFeaturesKHR; +#endif + physicalDevice4444FormatsFeaturesEXT.pNext = pNext; + pNext = &physicalDevice4444FormatsFeaturesEXT; + physicalDeviceSubpassShadingFeaturesHUAWEI.pNext = pNext; + pNext = &physicalDeviceSubpassShadingFeaturesHUAWEI; + physicalDeviceClusterCullingShaderFeaturesHUAWEI.pNext = pNext; + pNext = &physicalDeviceClusterCullingShaderFeaturesHUAWEI; + physicalDeviceShaderImageAtomicInt64FeaturesEXT.pNext = pNext; + pNext = &physicalDeviceShaderImageAtomicInt64FeaturesEXT; + physicalDeviceFragmentShadingRateFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceFragmentShadingRateFeaturesKHR; + physicalDeviceShaderTerminateInvocationFeatures.pNext = pNext; + pNext = &physicalDeviceShaderTerminateInvocationFeatures; + physicalDeviceFragmentShadingRateEnumsFeaturesNV.pNext = pNext; + pNext = &physicalDeviceFragmentShadingRateEnumsFeaturesNV; + physicalDeviceImage2DViewOf3DFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceImage2DViewOf3DFeaturesEXT; + physicalDeviceImageSlicedViewOf3DFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceImageSlicedViewOf3DFeaturesEXT; + physicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT; + physicalDeviceMutableDescriptorTypeFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceMutableDescriptorTypeFeaturesEXT; + physicalDeviceDepthClipControlFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceDepthClipControlFeaturesEXT; + physicalDeviceVertexInputDynamicStateFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceVertexInputDynamicStateFeaturesEXT; + physicalDeviceExternalMemoryRDMAFeaturesNV.pNext = pNext; + pNext = &physicalDeviceExternalMemoryRDMAFeaturesNV; + physicalDeviceColorWriteEnableFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceColorWriteEnableFeaturesEXT; + physicalDeviceSynchronization2Features.pNext = pNext; + pNext = &physicalDeviceSynchronization2Features; + physicalDeviceHostImageCopyFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceHostImageCopyFeaturesEXT; + physicalDevicePrimitivesGeneratedQueryFeaturesEXT.pNext = pNext; + pNext = &physicalDevicePrimitivesGeneratedQueryFeaturesEXT; + physicalDeviceLegacyDitheringFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceLegacyDitheringFeaturesEXT; + physicalDeviceMultisampledRenderToSingleSampledFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceMultisampledRenderToSingleSampledFeaturesEXT; + physicalDevicePipelineProtectedAccessFeaturesEXT.pNext = pNext; + pNext = &physicalDevicePipelineProtectedAccessFeaturesEXT; + physicalDeviceVideoMaintenance1FeaturesKHR.pNext = pNext; + pNext = &physicalDeviceVideoMaintenance1FeaturesKHR; + physicalDeviceInheritedViewportScissorFeaturesNV.pNext = pNext; + pNext = &physicalDeviceInheritedViewportScissorFeaturesNV; + physicalDeviceYcbcr2Plane444FormatsFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceYcbcr2Plane444FormatsFeaturesEXT; + physicalDeviceProvokingVertexFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceProvokingVertexFeaturesEXT; + physicalDeviceDescriptorBufferFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceDescriptorBufferFeaturesEXT; + physicalDeviceShaderIntegerDotProductFeatures.pNext = pNext; + pNext = &physicalDeviceShaderIntegerDotProductFeatures; + physicalDeviceFragmentShaderBarycentricFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceFragmentShaderBarycentricFeaturesKHR; + physicalDeviceRayTracingMotionBlurFeaturesNV.pNext = pNext; + pNext = &physicalDeviceRayTracingMotionBlurFeaturesNV; + physicalDeviceRGBA10X6FormatsFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceRGBA10X6FormatsFeaturesEXT; + physicalDeviceDynamicRenderingFeatures.pNext = pNext; + pNext = &physicalDeviceDynamicRenderingFeatures; + physicalDeviceImageViewMinLodFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceImageViewMinLodFeaturesEXT; + physicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT; + physicalDeviceLinearColorAttachmentFeaturesNV.pNext = pNext; + pNext = &physicalDeviceLinearColorAttachmentFeaturesNV; + physicalDeviceGraphicsPipelineLibraryFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceGraphicsPipelineLibraryFeaturesEXT; + physicalDeviceDescriptorSetHostMappingFeaturesVALVE.pNext = pNext; + pNext = &physicalDeviceDescriptorSetHostMappingFeaturesVALVE; + physicalDeviceNestedCommandBufferFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceNestedCommandBufferFeaturesEXT; + physicalDeviceShaderModuleIdentifierFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceShaderModuleIdentifierFeaturesEXT; + physicalDeviceImageCompressionControlFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceImageCompressionControlFeaturesEXT; + physicalDeviceImageCompressionControlSwapchainFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceImageCompressionControlSwapchainFeaturesEXT; + physicalDeviceSubpassMergeFeedbackFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceSubpassMergeFeedbackFeaturesEXT; + physicalDeviceOpacityMicromapFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceOpacityMicromapFeaturesEXT; +#ifdef VK_ENABLE_BETA_EXTENSIONS + physicalDeviceDisplacementMicromapFeaturesNV.pNext = pNext; + pNext = &physicalDeviceDisplacementMicromapFeaturesNV; +#endif + physicalDevicePipelinePropertiesFeaturesEXT.pNext = pNext; + pNext = &physicalDevicePipelinePropertiesFeaturesEXT; + physicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD.pNext = pNext; + pNext = &physicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD; + physicalDeviceNonSeamlessCubeMapFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceNonSeamlessCubeMapFeaturesEXT; + physicalDevicePipelineRobustnessFeaturesEXT.pNext = pNext; + pNext = &physicalDevicePipelineRobustnessFeaturesEXT; + physicalDeviceImageProcessingFeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceImageProcessingFeaturesQCOM; + physicalDeviceTilePropertiesFeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceTilePropertiesFeaturesQCOM; + physicalDeviceAmigoProfilingFeaturesSEC.pNext = pNext; + pNext = &physicalDeviceAmigoProfilingFeaturesSEC; + physicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT; + physicalDeviceDepthClampZeroOneFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceDepthClampZeroOneFeaturesEXT; + physicalDeviceAddressBindingReportFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceAddressBindingReportFeaturesEXT; + physicalDeviceOpticalFlowFeaturesNV.pNext = pNext; + pNext = &physicalDeviceOpticalFlowFeaturesNV; + physicalDeviceFaultFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceFaultFeaturesEXT; + physicalDevicePipelineLibraryGroupHandlesFeaturesEXT.pNext = pNext; + pNext = &physicalDevicePipelineLibraryGroupHandlesFeaturesEXT; + physicalDeviceShaderCoreBuiltinsFeaturesARM.pNext = pNext; + pNext = &physicalDeviceShaderCoreBuiltinsFeaturesARM; + physicalDeviceFrameBoundaryFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceFrameBoundaryFeaturesEXT; + physicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT; + physicalDeviceSwapchainMaintenance1FeaturesEXT.pNext = pNext; + pNext = &physicalDeviceSwapchainMaintenance1FeaturesEXT; + physicalDeviceDepthBiasControlFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceDepthBiasControlFeaturesEXT; + physicalDeviceRayTracingInvocationReorderFeaturesNV.pNext = pNext; + pNext = &physicalDeviceRayTracingInvocationReorderFeaturesNV; + physicalDeviceExtendedSparseAddressSpaceFeaturesNV.pNext = pNext; + pNext = &physicalDeviceExtendedSparseAddressSpaceFeaturesNV; + physicalDeviceMultiviewPerViewViewportsFeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceMultiviewPerViewViewportsFeaturesQCOM; + physicalDeviceRayTracingPositionFetchFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceRayTracingPositionFetchFeaturesKHR; + physicalDeviceMultiviewPerViewRenderAreasFeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceMultiviewPerViewRenderAreasFeaturesQCOM; + physicalDeviceShaderObjectFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceShaderObjectFeaturesEXT; + physicalDeviceShaderTileImageFeaturesEXT.pNext = pNext; + pNext = &physicalDeviceShaderTileImageFeaturesEXT; +#ifdef VK_USE_PLATFORM_SCREEN_QNX + physicalDeviceExternalMemoryScreenBufferFeaturesQNX.pNext = pNext; + pNext = &physicalDeviceExternalMemoryScreenBufferFeaturesQNX; +#endif + physicalDeviceCooperativeMatrixFeaturesKHR.pNext = pNext; + pNext = &physicalDeviceCooperativeMatrixFeaturesKHR; +#ifdef VK_ENABLE_BETA_EXTENSIONS + physicalDeviceShaderEnqueueFeaturesAMDX.pNext = pNext; + pNext = &physicalDeviceShaderEnqueueFeaturesAMDX; +#endif + physicalDeviceCubicClampFeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceCubicClampFeaturesQCOM; + physicalDeviceYcbcrDegammaFeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceYcbcrDegammaFeaturesQCOM; + physicalDeviceCubicWeightsFeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceCubicWeightsFeaturesQCOM; + physicalDeviceImageProcessing2FeaturesQCOM.pNext = pNext; + pNext = &physicalDeviceImageProcessing2FeaturesQCOM; + physicalDeviceDescriptorPoolOverallocationFeaturesNV.pNext = pNext; + pNext = &physicalDeviceDescriptorPoolOverallocationFeaturesNV; + physicalDevicePerStageDescriptorSetFeaturesNV.pNext = pNext; + pNext = &physicalDevicePerStageDescriptorSetFeaturesNV; +#ifdef VK_USE_PLATFORM_ANDROID_KHR + physicalDeviceExternalFormatResolveFeaturesANDROID.pNext = pNext; + pNext = &physicalDeviceExternalFormatResolveFeaturesANDROID; +#endif + physicalDeviceCudaKernelLaunchFeaturesNV.pNext = pNext; + pNext = &physicalDeviceCudaKernelLaunchFeaturesNV; + physicalDeviceSchedulingControlsFeaturesARM.pNext = pNext; + pNext = &physicalDeviceSchedulingControlsFeaturesARM; + physicalDeviceRelaxedLineRasterizationFeaturesIMG.pNext = pNext; + pNext = &physicalDeviceRelaxedLineRasterizationFeaturesIMG; + physicalDeviceRenderPassStripedFeaturesARM.pNext = pNext; + pNext = &physicalDeviceRenderPassStripedFeaturesARM; + physicalDeviceFeatures2KHR.pNext = pNext; + + } + + + VkPhysicalDeviceFeatures2KHR requiredFeaturesChain{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, nullptr}; + VkBaseOutStructure* current = nullptr; + + void ApplyRobustness(const VpDeviceCreateInfo* pCreateInfo) { +#ifdef VK_VERSION_1_1 + VkPhysicalDeviceFeatures2KHR* pFeatures2 = static_cast<VkPhysicalDeviceFeatures2KHR*>( + vpGetStructure(&this->requiredFeaturesChain, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR)); + if (pFeatures2 != nullptr && (pCreateInfo->flags & VP_DEVICE_CREATE_DISABLE_ROBUST_BUFFER_ACCESS_BIT)) { + pFeatures2->features.robustBufferAccess = VK_FALSE; + } +#endif + +#ifdef VK_EXT_robustness2 + VkPhysicalDeviceRobustness2FeaturesEXT* pRobustness2FeaturesEXT = static_cast<VkPhysicalDeviceRobustness2FeaturesEXT*>( + vpGetStructure(&this->requiredFeaturesChain, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT)); + if (pRobustness2FeaturesEXT != nullptr) { + if (pCreateInfo->flags & VP_DEVICE_CREATE_DISABLE_ROBUST_BUFFER_ACCESS_BIT) { + pRobustness2FeaturesEXT->robustBufferAccess2 = VK_FALSE; + } + if (pCreateInfo->flags & VP_DEVICE_CREATE_DISABLE_ROBUST_IMAGE_ACCESS_BIT) { + pRobustness2FeaturesEXT->robustImageAccess2 = VK_FALSE; + } + } +#endif +#ifdef VK_EXT_image_robustness + VkPhysicalDeviceImageRobustnessFeaturesEXT* pImageRobustnessFeaturesEXT = + static_cast<VkPhysicalDeviceImageRobustnessFeaturesEXT*>(vpGetStructure( + &this->requiredFeaturesChain, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT)); + if (pImageRobustnessFeaturesEXT != nullptr && (pCreateInfo->flags & VP_DEVICE_CREATE_DISABLE_ROBUST_IMAGE_ACCESS_BIT)) { + pImageRobustnessFeaturesEXT->robustImageAccess = VK_FALSE; + } +#endif +#ifdef VK_VERSION_1_3 + VkPhysicalDeviceVulkan13Features* pVulkan13Features = static_cast<VkPhysicalDeviceVulkan13Features*>( + vpGetStructure(&this->requiredFeaturesChain, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES)); + if (pVulkan13Features != nullptr && (pCreateInfo->flags & VP_DEVICE_CREATE_DISABLE_ROBUST_IMAGE_ACCESS_BIT)) { + pVulkan13Features->robustImageAccess = VK_FALSE; + } +#endif + } + + void ApplyFeatures(const VpDeviceCreateInfo* pCreateInfo) { + const std::size_t offset = sizeof(VkBaseOutStructure); + const VkBaseOutStructure* q = reinterpret_cast<const VkBaseOutStructure*>(pCreateInfo->pCreateInfo->pNext); + while (q) { + std::size_t count = this->structureSize[q->sType]; + for (std::size_t i = 0, n = count; i < n; ++i) { + const VkBaseOutStructure* pInputStruct = reinterpret_cast<const VkBaseOutStructure*>(q); + VkBaseOutStructure* pOutputStruct = reinterpret_cast<VkBaseOutStructure*>(detail::vpGetStructure(&this->requiredFeaturesChain, q->sType)); + const uint8_t* pInputData = reinterpret_cast<const uint8_t*>(pInputStruct) + offset; + uint8_t* pOutputData = reinterpret_cast<uint8_t*>(pOutputStruct) + offset; + const VkBool32* input = reinterpret_cast<const VkBool32*>(pInputData); + VkBool32* output = reinterpret_cast<VkBool32*>(pOutputData); + + output[i] = (output[i] == VK_TRUE || input[i] == VK_TRUE) ? VK_TRUE : VK_FALSE; + } + q = q->pNext; + } + + this->ApplyRobustness(pCreateInfo); + } + + void PushBack(VkBaseOutStructure* found) { + VkBaseOutStructure* last = reinterpret_cast<VkBaseOutStructure*>(&requiredFeaturesChain); + while (last->pNext != nullptr) { + last = last->pNext; + } + last->pNext = found; + } + + void Build(const std::vector<VkStructureType>& requiredList) { + for (std::size_t i = 0, n = requiredList.size(); i < n; ++i) { + const VkStructureType sType = requiredList[i]; + if (sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR) { + continue; + } + + VkBaseOutStructure* found = vpExtractStructure(&physicalDeviceFeatures2KHR, sType); + if (found == nullptr) { + continue; + } + + PushBack(found); + } + } +}; // struct FeaturesChain + +VPAPI_ATTR const VpProfileDesc* vpGetProfileDesc(const char profileName[VP_MAX_PROFILE_NAME_SIZE]) { + for (uint32_t i = 0; i < profileCount; ++i) { + if (strncmp(profiles[i].props.profileName, profileName, VP_MAX_PROFILE_NAME_SIZE) == 0) return &profiles[i]; + } + return nullptr; +} + +VPAPI_ATTR std::vector<VpProfileProperties> GatherProfiles(const VpProfileProperties& profile, const char* pBlockName = nullptr) { + std::vector<VpProfileProperties> profiles; + + if (pBlockName == nullptr) { + const detail::VpProfileDesc* profile_desc = detail::vpGetProfileDesc(profile.profileName); + if (profile_desc != nullptr) { + for (uint32_t profile_index = 0; profile_index < profile_desc->requiredProfileCount; ++profile_index) { + profiles.push_back(profile_desc->pRequiredProfiles[profile_index]); + } + } + } + + profiles.push_back(profile); + + return profiles; +} + +VPAPI_ATTR bool vpCheckVersion(uint32_t actual, uint32_t expected) { + uint32_t actualMajor = VK_API_VERSION_MAJOR(actual); + uint32_t actualMinor = VK_API_VERSION_MINOR(actual); + uint32_t expectedMajor = VK_API_VERSION_MAJOR(expected); + uint32_t expectedMinor = VK_API_VERSION_MINOR(expected); + return actualMajor > expectedMajor || (actualMajor == expectedMajor && actualMinor >= expectedMinor); +} + +VPAPI_ATTR bool HasExtension(const std::vector<VkExtensionProperties>& list, const VkExtensionProperties& element) { + for (std::size_t i = 0, n = list.size(); i < n; ++i) { + if (strcmp(list[i].extensionName, element.extensionName) == 0) { + return true; + } + } + + return false; +} + +VPAPI_ATTR bool CheckExtension(const VkExtensionProperties* supportedProperties, size_t supportedSize, const char *requestedExtension) { + bool found = false; + for (size_t i = 0, n = supportedSize; i < n; ++i) { + if (strcmp(supportedProperties[i].extensionName, requestedExtension) == 0) { + found = true; + break; + // Drivers don't actually update their spec version, so we cannot rely on this + // if (supportedProperties[i].specVersion >= expectedVersion) found = true; + } + } + VP_DEBUG_COND_MSGF(!found, "Unsupported extension: %s", requestedExtension); + return found; +} + +VPAPI_ATTR bool CheckExtension(const std::vector<const char*>& extensions, const char* extension) { + for (const char* c : extensions) { + if (strcmp(c, extension) == 0) { + return true; + } + } + return false; +} + +VPAPI_ATTR void GetExtensions(uint32_t extensionCount, const VkExtensionProperties *pExtensions, std::vector<const char *> &extensions) { + for (uint32_t i = 0; i < extensionCount; ++i) { + if (CheckExtension(extensions, pExtensions[i].extensionName)) { + continue; + } + extensions.push_back(pExtensions[i].extensionName); + } +} + +VPAPI_ATTR std::vector<VpBlockProperties> GatherBlocks( + uint32_t enabledFullProfileCount, const VpProfileProperties* pEnabledFullProfiles, + uint32_t enabledProfileBlockCount, const VpBlockProperties* pEnabledProfileBlocks) { + std::vector<VpBlockProperties> results; + + for (std::size_t i = 0; i < enabledFullProfileCount; ++i) { + const std::vector<VpProfileProperties>& profiles = GatherProfiles(pEnabledFullProfiles[i]); + + for (std::size_t j = 0; j < profiles.size(); ++j) { + VpBlockProperties block{profiles[j], 0, ""}; + results.push_back(block); + } + } + + for (std::size_t i = 0; i < enabledProfileBlockCount; ++i) { + results.push_back(pEnabledProfileBlocks[i]); + } + + return results; +} + +VPAPI_ATTR VkResult vpGetInstanceProfileSupportSingleProfile( + uint32_t api_version, const std::vector<VkExtensionProperties>& supported_extensions, + const VpProfileProperties* pProfile, VkBool32* pSupported, std::vector<VpBlockProperties>& supportedBlocks, std::vector<VpBlockProperties>& unsupportedBlocks) { + assert(pProfile != nullptr); + + const detail::VpProfileDesc* pProfileDesc = vpGetProfileDesc(pProfile->profileName); + if (pProfileDesc == nullptr) { + *pSupported = VK_FALSE; + return VK_ERROR_UNKNOWN; + } + + VpBlockProperties block{*pProfile, api_version}; + + if (pProfileDesc->props.specVersion < pProfile->specVersion) { + *pSupported = VK_FALSE; + unsupportedBlocks.push_back(block); + } + + // Required API version is built in root profile, not need to check dependent profile API versions + if (api_version != 0) { + if (!vpCheckVersion(api_version, pProfileDesc->minApiVersion)) { + const uint32_t version_min_major = VK_API_VERSION_MAJOR(pProfileDesc->minApiVersion); + const uint32_t version_min_minor = VK_API_VERSION_MINOR(pProfileDesc->minApiVersion); + const uint32_t version_min_patch = VK_API_VERSION_PATCH(pProfileDesc->minApiVersion); + + const uint32_t version_major = VK_API_VERSION_MAJOR(api_version); + const uint32_t version_minor = VK_API_VERSION_MINOR(api_version); + const uint32_t version_patch = VK_API_VERSION_PATCH(api_version); + + VP_DEBUG_MSGF("Unsupported Profile API version %u.%u.%u on a Vulkan system with version %u.%u.%u", version_min_major, version_min_minor, version_min_patch, version_major, version_minor, version_patch); + + *pSupported = VK_FALSE; + unsupportedBlocks.push_back(block); + } + } + + for (uint32_t capability_index = 0; capability_index < pProfileDesc->requiredCapabilityCount; ++capability_index) { + const VpCapabilitiesDesc& capabilities_desc = pProfileDesc->pRequiredCapabilities[capability_index]; + + VkBool32 supported_capabilities = VK_FALSE; + for (uint32_t variant_index = 0; variant_index < capabilities_desc.variantCount; ++variant_index) { + const VpVariantDesc& variant_desc = capabilities_desc.pVariants[variant_index]; + + VkBool32 supported_variant = VK_TRUE; + for (uint32_t i = 0; i < variant_desc.instanceExtensionCount; ++i) { + if (!detail::CheckExtension(supported_extensions.data(), supported_extensions.size(), + variant_desc.pInstanceExtensions[i].extensionName)) { + supported_variant = VK_FALSE; + memcpy(block.blockName, variant_desc.blockName, VP_MAX_PROFILE_NAME_SIZE * sizeof(char)); + unsupportedBlocks.push_back(block); + } + } + + if (supported_variant == VK_TRUE) { + supported_capabilities = VK_TRUE; + memcpy(block.blockName, variant_desc.blockName, VP_MAX_PROFILE_NAME_SIZE * sizeof(char)); + supportedBlocks.push_back(block); + } + } + + if (supported_capabilities == VK_FALSE) { + *pSupported = VK_FALSE; + return VK_SUCCESS; + } + } + + return VK_SUCCESS; +} + +enum structure_type { + STRUCTURE_FEATURE = 0, + STRUCTURE_PROPERTY, + STRUCTURE_FORMAT +}; + +VPAPI_ATTR VkResult vpGetProfileStructureTypes(const VpProfileProperties *pProfile, const char* pBlockName, structure_type type, uint32_t *pStructureTypeCount, VkStructureType *pStructureTypes) { + VkResult result = pBlockName == nullptr ? VK_SUCCESS : VK_INCOMPLETE; + + std::vector<VkStructureType> results; + + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile); + + for (std::size_t profile_index = 0, profile_count = profiles.size(); profile_index < profile_count; ++profile_index) { + const detail::VpProfileDesc* profile_desc = detail::vpGetProfileDesc(profiles[profile_index].profileName); + if (profile_desc == nullptr) return VK_ERROR_UNKNOWN; + + for (uint32_t capability_index = 0; capability_index < profile_desc->requiredCapabilityCount; ++capability_index) { + const detail::VpCapabilitiesDesc& capabilities = profile_desc->pRequiredCapabilities[capability_index]; + + for (uint32_t variant_index = 0; variant_index < capabilities.variantCount; ++variant_index) { + const detail::VpVariantDesc& variant = capabilities.pVariants[variant_index]; + if (pBlockName != nullptr) { + if (strcmp(variant.blockName, pBlockName) != 0) { + continue; + } + result = VK_SUCCESS; + } + + uint32_t count = 0; + const VkStructureType* data = nullptr; + + switch (type) { + default: + case STRUCTURE_FEATURE: + count = variant.featureStructTypeCount; + data = variant.pFeatureStructTypes; + break; + case STRUCTURE_PROPERTY: + count = variant.propertyStructTypeCount; + data = variant.pPropertyStructTypes; + break; + case STRUCTURE_FORMAT: + count = variant.formatStructTypeCount; + data = variant.pFormatStructTypes; + break; + } + + for (uint32_t i = 0; i < count; ++i) { + const VkStructureType type = data[i]; + if (std::find(results.begin(), results.end(), type) == std::end(results)) { + results.push_back(type); + } + } + } + } + } + + const uint32_t count = static_cast<uint32_t>(results.size()); + std::sort(results.begin(), results.end()); + + if (pStructureTypes == nullptr) { + *pStructureTypeCount = count; + } else { + if (*pStructureTypeCount < count) { + result = VK_INCOMPLETE; + } else { + *pStructureTypeCount = count; + } + + if (*pStructureTypeCount > 0) { + memcpy(pStructureTypes, &results[0], *pStructureTypeCount * sizeof(VkStructureType)); + } + } + + return result; +} + +enum ExtensionType { + EXTENSION_INSTANCE, + EXTENSION_DEVICE, +}; + +VPAPI_ATTR VkResult vpGetProfileExtensionProperties(const VpProfileProperties *pProfile, const char* pBlockName, ExtensionType type, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) { + VkResult result = pBlockName == nullptr ? VK_SUCCESS : VK_INCOMPLETE; + + std::vector<VkExtensionProperties> results; + + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile, pBlockName); + + for (std::size_t profile_index = 0, profile_count = profiles.size(); profile_index < profile_count; ++profile_index) { + const detail::VpProfileDesc* profile_desc = detail::vpGetProfileDesc(profiles[profile_index].profileName); + if (profile_desc == nullptr) return VK_ERROR_UNKNOWN; + + for (uint32_t capability_index = 0; capability_index < profile_desc->requiredCapabilityCount; ++capability_index) { + const detail::VpCapabilitiesDesc& capabilities = profile_desc->pRequiredCapabilities[capability_index]; + + for (uint32_t variant_index = 0; variant_index < capabilities.variantCount; ++variant_index) { + const detail::VpVariantDesc& variant = capabilities.pVariants[variant_index]; + if (pBlockName != nullptr) { + if (strcmp(variant.blockName, pBlockName) != 0) { + continue; + } + result = VK_SUCCESS; + } + + switch (type) { + default: + case EXTENSION_INSTANCE: + for (uint32_t i = 0; i < variant.instanceExtensionCount; ++i) { + if (detail::HasExtension(results, variant.pInstanceExtensions[i])) { + continue; + } + results.push_back(variant.pInstanceExtensions[i]); + } + break; + case EXTENSION_DEVICE: + for (uint32_t i = 0; i < variant.deviceExtensionCount; ++i) { + if (detail::HasExtension(results, variant.pDeviceExtensions[i])) { + continue; + } + results.push_back(variant.pDeviceExtensions[i]); + } + break; + } + } + } + } + + const uint32_t count = static_cast<uint32_t>(results.size()); + + if (pProperties == nullptr) { + *pPropertyCount = count; + } else { + if (*pPropertyCount < count) { + result = VK_INCOMPLETE; + } else { + *pPropertyCount = count; + } + if (*pPropertyCount > 0) { + memcpy(pProperties, &results[0], *pPropertyCount * sizeof(VkExtensionProperties)); + } + } + + return result; +} + +} // namespace detail + +VPAPI_ATTR VkResult vpGetProfiles(uint32_t *pPropertyCount, VpProfileProperties *pProperties) { + VkResult result = VK_SUCCESS; + + if (pProperties == nullptr) { + *pPropertyCount = detail::profileCount; + } else { + if (*pPropertyCount < detail::profileCount) { + result = VK_INCOMPLETE; + } else { + *pPropertyCount = detail::profileCount; + } + for (uint32_t i = 0; i < *pPropertyCount; ++i) { + pProperties[i] = detail::profiles[i].props; + } + } + return result; +} + +VPAPI_ATTR VkResult vpGetProfileRequiredProfiles(const VpProfileProperties *pProfile, uint32_t *pPropertyCount, VpProfileProperties *pProperties) { + VkResult result = VK_SUCCESS; + + const detail::VpProfileDesc* pDesc = detail::vpGetProfileDesc(pProfile->profileName); + if (pDesc == nullptr) return VK_ERROR_UNKNOWN; + + if (pProperties == nullptr) { + *pPropertyCount = pDesc->requiredProfileCount; + } else { + if (*pPropertyCount < pDesc->requiredProfileCount) { + result = VK_INCOMPLETE; + } else { + *pPropertyCount = pDesc->requiredProfileCount; + } + for (uint32_t i = 0; i < *pPropertyCount; ++i) { + pProperties[i] = pDesc->pRequiredProfiles[i]; + } + } + return result; +} + +VPAPI_ATTR uint32_t vpGetProfileAPIVersion(const VpProfileProperties* pProfile) { + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile, nullptr); + + uint32_t major = 0; + uint32_t minor = 0; + uint32_t patch = 0; + + for (std::size_t i = 0, n = profiles.size(); i < n; ++i) { + const detail::VpProfileDesc* pDesc = detail::vpGetProfileDesc(profiles[i].profileName); + if (pDesc == nullptr) return 0; + + major = std::max<uint32_t>(major, VK_API_VERSION_MAJOR(pDesc->minApiVersion)); + minor = std::max<uint32_t>(minor, VK_API_VERSION_MINOR(pDesc->minApiVersion)); + patch = std::max<uint32_t>(patch, VK_API_VERSION_PATCH(pDesc->minApiVersion)); + } + + return VK_MAKE_API_VERSION(0, major, minor, patch); +} + +VPAPI_ATTR VkResult vpGetProfileFallbacks(const VpProfileProperties *pProfile, uint32_t *pPropertyCount, VpProfileProperties *pProperties) { + VkResult result = VK_SUCCESS; + + const detail::VpProfileDesc* pDesc = detail::vpGetProfileDesc(pProfile->profileName); + if (pDesc == nullptr) return VK_ERROR_UNKNOWN; + + if (pProperties == nullptr) { + *pPropertyCount = pDesc->fallbackCount; + } else { + if (*pPropertyCount < pDesc->fallbackCount) { + result = VK_INCOMPLETE; + } else { + *pPropertyCount = pDesc->fallbackCount; + } + for (uint32_t i = 0; i < *pPropertyCount; ++i) { + pProperties[i] = pDesc->pFallbacks[i]; + } + } + return result; +} + +VPAPI_ATTR VkResult vpHasMultipleVariantsProfile(const VpProfileProperties *pProfile, VkBool32 *pHasMultipleVariants) { + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile, nullptr); + + for (std::size_t profile_index = 0, profile_count = profiles.size(); profile_index < profile_count; ++profile_index) { + const detail::VpProfileDesc* pDesc = detail::vpGetProfileDesc(profiles[profile_index].profileName); + if (pDesc == nullptr) return VK_ERROR_UNKNOWN; + + for (uint32_t capabilities_index = 0, n = pDesc->requiredCapabilityCount; capabilities_index < n; ++capabilities_index) { + if (pDesc->pRequiredCapabilities[capabilities_index].variantCount > 1) { + *pHasMultipleVariants = VK_TRUE; + return VK_SUCCESS; + } + } + } + + *pHasMultipleVariants = VK_FALSE; + return VK_SUCCESS; +} + +VPAPI_ATTR VkResult vpGetInstanceProfileVariantsSupport(const char *pLayerName, const VpProfileProperties *pProfile, VkBool32 *pSupported, uint32_t *pPropertyCount, VpBlockProperties* pProperties) { + VkResult result = VK_SUCCESS; + + uint32_t api_version = VK_MAKE_API_VERSION(0, 1, 0, 0); + static PFN_vkEnumerateInstanceVersion pfnEnumerateInstanceVersion = + (PFN_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"); + if (pfnEnumerateInstanceVersion != nullptr) { + result = pfnEnumerateInstanceVersion(&api_version); + if (result != VK_SUCCESS) { + *pSupported = VK_FALSE; + return result; + } + } + + uint32_t supported_instance_extension_count = 0; + result = vkEnumerateInstanceExtensionProperties(pLayerName, &supported_instance_extension_count, nullptr); + if (result != VK_SUCCESS) { + *pSupported = VK_FALSE; + return result; + } + std::vector<VkExtensionProperties> supported_instance_extensions; + if (supported_instance_extension_count > 0) { + supported_instance_extensions.resize(supported_instance_extension_count); + } + result = vkEnumerateInstanceExtensionProperties(pLayerName, &supported_instance_extension_count, supported_instance_extensions.data()); + if (result != VK_SUCCESS) { + *pSupported = VK_FALSE; + return result; + } + + VkBool32 supported = VK_TRUE; + + // We require VK_KHR_get_physical_device_properties2 if we are on Vulkan 1.0 + if (api_version < VK_API_VERSION_1_1) { + bool foundGPDP2 = false; + for (size_t i = 0; i < supported_instance_extensions.size(); ++i) { + if (strcmp(supported_instance_extensions[i].extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0) { + foundGPDP2 = true; + break; + } + } + if (!foundGPDP2) { + VP_DEBUG_MSG("Unsupported mandatory extension VK_KHR_get_physical_device_properties2 on Vulkan 1.0"); + supported = VK_FALSE; + } + } + + const detail::VpProfileDesc* pProfileDesc = detail::vpGetProfileDesc(pProfile->profileName); + if (pProfileDesc == nullptr) return VK_ERROR_UNKNOWN; + + std::vector<VpBlockProperties> supported_blocks; + std::vector<VpBlockProperties> unsupported_blocks; + + result = detail::vpGetInstanceProfileSupportSingleProfile(api_version, supported_instance_extensions, pProfile, &supported, supported_blocks, unsupported_blocks); + if (result != VK_SUCCESS) { + *pSupported = supported; + return result; + } + + for (std::size_t i = 0; i < pProfileDesc->requiredProfileCount; ++i) { + result = detail::vpGetInstanceProfileSupportSingleProfile(0, supported_instance_extensions, &pProfileDesc->pRequiredProfiles[i], &supported, supported_blocks, unsupported_blocks); + if (result != VK_SUCCESS) { + *pSupported = supported; + return result; + } + } + + const std::vector<VpBlockProperties>& blocks = supported ? supported_blocks : unsupported_blocks; + + if (pProperties == nullptr) { + *pPropertyCount = static_cast<uint32_t>(blocks.size()); + } else { + if (*pPropertyCount < static_cast<uint32_t>(blocks.size())) { + result = VK_INCOMPLETE; + } else { + *pPropertyCount = static_cast<uint32_t>(blocks.size()); + } + for (uint32_t i = 0, n = static_cast<uint32_t>(blocks.size()); i < n; ++i) { + pProperties[i] = blocks[i]; + } + } + + *pSupported = supported; + return result; +} + +VPAPI_ATTR VkResult vpGetInstanceProfileSupport(const char *pLayerName, const VpProfileProperties *pProfile, VkBool32 *pSupported) { + uint32_t count = 0; + return vpGetInstanceProfileVariantsSupport(pLayerName, pProfile, pSupported, &count, nullptr); +} + + +VPAPI_ATTR VkResult vpCreateInstance(const VpInstanceCreateInfo *pCreateInfo, + const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) { + if (pCreateInfo == nullptr || pInstance == nullptr) { + return vkCreateInstance(pCreateInfo == nullptr ? nullptr : pCreateInfo->pCreateInfo, pAllocator, pInstance); + } + + const std::vector<VpBlockProperties>& blocks = detail::GatherBlocks( + pCreateInfo->enabledFullProfileCount, pCreateInfo->pEnabledFullProfiles, + pCreateInfo->enabledProfileBlockCount, pCreateInfo->pEnabledProfileBlocks); + + std::vector<const char*> extensions; + for (std::uint32_t i = 0, n = pCreateInfo->pCreateInfo->enabledExtensionCount; i < n; ++i) { + extensions.push_back(pCreateInfo->pCreateInfo->ppEnabledExtensionNames[i]); + } + + for (std::size_t i = 0, n = blocks.size(); i < n; ++i) { + const detail::VpProfileDesc* pProfileDesc = detail::vpGetProfileDesc(blocks[i].profiles.profileName); + if (pProfileDesc == nullptr) return VK_ERROR_UNKNOWN; + + for (std::size_t j = 0, p = pProfileDesc->requiredCapabilityCount; j < p; ++j) { + const detail::VpCapabilitiesDesc* pCapsDesc = &pProfileDesc->pRequiredCapabilities[j]; + + for (std::size_t v = 0, q = pCapsDesc->variantCount; v < q; ++v) { + const detail::VpVariantDesc* variant = &pCapsDesc->pVariants[v]; + + if (strcmp(blocks[i].blockName, "") != 0) { + if (strcmp(variant->blockName, blocks[i].blockName) != 0) { + continue; + } + } + + detail::GetExtensions(variant->instanceExtensionCount, variant->pInstanceExtensions, extensions); + } + } + } + + VkApplicationInfo appInfo{VK_STRUCTURE_TYPE_APPLICATION_INFO}; + if (pCreateInfo->pCreateInfo->pApplicationInfo != nullptr) { + appInfo = *pCreateInfo->pCreateInfo->pApplicationInfo; + } else if (!blocks.empty()) { + appInfo.apiVersion = vpGetProfileAPIVersion(&blocks[0].profiles); + } + + VkInstanceCreateInfo createInfo = *pCreateInfo->pCreateInfo; + createInfo.pApplicationInfo = &appInfo; + + // Need to include VK_KHR_get_physical_device_properties2 if we are on Vulkan 1.0 + if (createInfo.pApplicationInfo->apiVersion < VK_API_VERSION_1_1) { + bool foundGPDP2 = false; + for (size_t i = 0; i < extensions.size(); ++i) { + if (strcmp(extensions[i], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0) { + foundGPDP2 = true; + break; + } + } + if (!foundGPDP2) { + extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + } + } + +#ifdef __APPLE__ + bool has_portability_ext = false; + for (std::size_t i = 0, n = extensions.size(); i < n; ++i) { + if (strcmp(extensions[i], VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) == 0) { + has_portability_ext = true; + break; + } + } + + if (!has_portability_ext) { + extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + } + + createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +#endif + + if (!extensions.empty()) { + createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + } + + return vkCreateInstance(&createInfo, pAllocator, pInstance); +} + +VPAPI_ATTR VkResult vpGetPhysicalDeviceProfileVariantsSupport(VkInstance instance, VkPhysicalDevice physicalDevice, + const VpProfileProperties *pProfile, VkBool32 *pSupported, uint32_t *pPropertyCount, VpBlockProperties* pProperties) { + VkResult result = VK_SUCCESS; + + uint32_t supported_device_extension_count = 0; + result = vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &supported_device_extension_count, nullptr); + if (result != VK_SUCCESS) { + return result; + } + std::vector<VkExtensionProperties> supported_device_extensions; + if (supported_device_extension_count > 0) { + supported_device_extensions.resize(supported_device_extension_count); + } + result = vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &supported_device_extension_count, supported_device_extensions.data()); + if (result != VK_SUCCESS) { + return result; + } + + // Workaround old loader bug where count could be smaller on the second call to vkEnumerateDeviceExtensionProperties + if (supported_device_extension_count > 0) { + supported_device_extensions.resize(supported_device_extension_count); + } + + const detail::VpProfileDesc* pProfileDesc = detail::vpGetProfileDesc(pProfile->profileName); + if (pProfileDesc == nullptr) return VK_ERROR_UNKNOWN; + + struct GPDP2EntryPoints { + PFN_vkGetPhysicalDeviceFeatures2KHR pfnGetPhysicalDeviceFeatures2; + PFN_vkGetPhysicalDeviceProperties2KHR pfnGetPhysicalDeviceProperties2; + PFN_vkGetPhysicalDeviceFormatProperties2KHR pfnGetPhysicalDeviceFormatProperties2; + PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR pfnGetPhysicalDeviceQueueFamilyProperties2; + }; + + std::vector<VpBlockProperties> supported_blocks; + std::vector<VpBlockProperties> unsupported_blocks; + + struct UserData { + VkPhysicalDevice physicalDevice; + std::vector<VpBlockProperties>& supported_blocks; + std::vector<VpBlockProperties>& unsupported_blocks; + const detail::VpVariantDesc* variant; + GPDP2EntryPoints gpdp2; + uint32_t index; + uint32_t count; + detail::PFN_vpStructChainerCb pfnCb; + bool supported; + } userData{physicalDevice, supported_blocks, unsupported_blocks}; + + // Attempt to load core versions of the GPDP2 entry points + userData.gpdp2.pfnGetPhysicalDeviceFeatures2 = + (PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2"); + userData.gpdp2.pfnGetPhysicalDeviceProperties2 = + (PFN_vkGetPhysicalDeviceProperties2KHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2"); + userData.gpdp2.pfnGetPhysicalDeviceFormatProperties2 = + (PFN_vkGetPhysicalDeviceFormatProperties2KHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2"); + userData.gpdp2.pfnGetPhysicalDeviceQueueFamilyProperties2 = + (PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties2"); + + // If not successful, try to load KHR variant + if (userData.gpdp2.pfnGetPhysicalDeviceFeatures2 == nullptr) { + userData.gpdp2.pfnGetPhysicalDeviceFeatures2 = + (PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2KHR"); + userData.gpdp2.pfnGetPhysicalDeviceProperties2 = + (PFN_vkGetPhysicalDeviceProperties2KHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR"); + userData.gpdp2.pfnGetPhysicalDeviceFormatProperties2 = + (PFN_vkGetPhysicalDeviceFormatProperties2KHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2KHR"); + userData.gpdp2.pfnGetPhysicalDeviceQueueFamilyProperties2 = + (PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties2KHR"); + } + + if (userData.gpdp2.pfnGetPhysicalDeviceFeatures2 == nullptr || + userData.gpdp2.pfnGetPhysicalDeviceProperties2 == nullptr || + userData.gpdp2.pfnGetPhysicalDeviceFormatProperties2 == nullptr || + userData.gpdp2.pfnGetPhysicalDeviceQueueFamilyProperties2 == nullptr) { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + VP_DEBUG_MSGF("Checking device support for profile %s (%s). You may find the details of the capabilities of this device on https://vulkan.gpuinfo.org/", pProfile->profileName, detail::vpGetDeviceAndDriverInfoString(physicalDevice, userData.gpdp2.pfnGetPhysicalDeviceProperties2).c_str()); + + bool supported = true; + + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile); + + for (std::size_t i = 0, n = profiles.size(); i < n; ++i) { + const char* profile_name = profiles[i].profileName; + + const detail::VpProfileDesc* pProfileDesc = detail::vpGetProfileDesc(profile_name); + if (pProfileDesc == nullptr) return VK_ERROR_UNKNOWN; + + bool supported_profile = true; + + + if (pProfileDesc->props.specVersion < pProfile->specVersion) { + supported_profile = false; + } + + VpBlockProperties block{profiles[i], pProfileDesc->minApiVersion}; + + VkPhysicalDeviceProperties props{}; + vkGetPhysicalDeviceProperties(physicalDevice, &props); + if (!detail::vpCheckVersion(props.apiVersion, pProfileDesc->minApiVersion)) { + VP_DEBUG_MSGF("Unsupported API version: %u.%u.%u", VK_API_VERSION_MAJOR(pProfileDesc->minApiVersion), VK_API_VERSION_MINOR(pProfileDesc->minApiVersion), VK_API_VERSION_PATCH(pProfileDesc->minApiVersion)); + supported_profile = false; + } + + for (uint32_t required_capability_index = 0; required_capability_index < pProfileDesc->requiredCapabilityCount; ++required_capability_index) { + const detail::VpCapabilitiesDesc* required_capabilities = &pProfileDesc->pRequiredCapabilities[required_capability_index]; + + bool supported_block = false; + + for (uint32_t variant_index = 0; variant_index < required_capabilities->variantCount; ++variant_index) { + const detail::VpVariantDesc& variant_desc = required_capabilities->pVariants[variant_index]; + + bool supported_variant = true; + + for (uint32_t i = 0; i < variant_desc.deviceExtensionCount; ++i) { + const char *requested_extension = variant_desc.pDeviceExtensions[i].extensionName; + if (!detail::CheckExtension(supported_device_extensions.data(), supported_device_extensions.size(), requested_extension)) { + supported_variant = false; + } + } + + userData.variant = &variant_desc; + + VkPhysicalDeviceFeatures2KHR features{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR }; + userData.variant->chainers.pfnFeature( + static_cast<VkBaseOutStructure*>(static_cast<void*>(&features)), &userData, + [](VkBaseOutStructure* p, void* pUser) { + UserData* pUserData = static_cast<UserData*>(pUser); + pUserData->gpdp2.pfnGetPhysicalDeviceFeatures2(pUserData->physicalDevice, + static_cast<VkPhysicalDeviceFeatures2KHR*>(static_cast<void*>(p))); + pUserData->supported = true; + while (p != nullptr) { + if (!pUserData->variant->feature.pfnComparator(p)) { + pUserData->supported = false; + } + p = p->pNext; + } + } + ); + if (!userData.supported) { + supported_variant = false; + } + + VkPhysicalDeviceProperties2KHR props{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR }; + userData.variant->chainers.pfnProperty( + static_cast<VkBaseOutStructure*>(static_cast<void*>(&props)), &userData, + [](VkBaseOutStructure* p, void* pUser) { + UserData* pUserData = static_cast<UserData*>(pUser); + pUserData->gpdp2.pfnGetPhysicalDeviceProperties2(pUserData->physicalDevice, + static_cast<VkPhysicalDeviceProperties2KHR*>(static_cast<void*>(p))); + pUserData->supported = true; + while (p != nullptr) { + if (!pUserData->variant->property.pfnComparator(p)) { + pUserData->supported = false; + } + p = p->pNext; + } + } + ); + if (!userData.supported) { + supported_variant = false; + } + + for (uint32_t i = 0; i < userData.variant->formatCount && supported_variant; ++i) { + userData.index = i; + VkFormatProperties2KHR props{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR }; + userData.variant->chainers.pfnFormat( + static_cast<VkBaseOutStructure*>(static_cast<void*>(&props)), &userData, + [](VkBaseOutStructure* p, void* pUser) { + UserData* pUserData = static_cast<UserData*>(pUser); + pUserData->gpdp2.pfnGetPhysicalDeviceFormatProperties2(pUserData->physicalDevice, pUserData->variant->pFormats[pUserData->index].format, + static_cast<VkFormatProperties2KHR*>(static_cast<void*>(p))); + pUserData->supported = true; + while (p != nullptr) { + if (!pUserData->variant->pFormats[pUserData->index].pfnComparator(p)) { + pUserData->supported = false; + } + p = p->pNext; + } + } + ); + if (!userData.supported) { + supported_variant = false; + } + } + + memcpy(block.blockName, variant_desc.blockName, VP_MAX_PROFILE_NAME_SIZE * sizeof(char)); + if (supported_variant) { + supported_blocks.push_back(block); + supported_block = true; + break; + } else { + unsupported_blocks.push_back(block); + } + } + + if (!supported_block) { + supported_profile = false; + } + } + + if (!supported_profile) { + supported = false; + } + } + + const std::vector<VpBlockProperties>& blocks = supported ? supported_blocks : unsupported_blocks; + + if (pProperties == nullptr) { + *pPropertyCount = static_cast<uint32_t>(blocks.size()); + } else { + if (*pPropertyCount < static_cast<uint32_t>(blocks.size())) { + result = VK_INCOMPLETE; + } else { + *pPropertyCount = static_cast<uint32_t>(blocks.size()); + } + for (uint32_t i = 0, n = static_cast<uint32_t>(blocks.size()); i < n; ++i) { + pProperties[i] = blocks[i]; + } + } + + *pSupported = supported ? VK_TRUE : VK_FALSE; + return VK_SUCCESS; +} + +VPAPI_ATTR VkResult vpGetPhysicalDeviceProfileSupport(VkInstance instance, VkPhysicalDevice physicalDevice, + const VpProfileProperties *pProfile, VkBool32 *pSupported) { + uint32_t count = 0; + return vpGetPhysicalDeviceProfileVariantsSupport(instance, physicalDevice, pProfile, pSupported, &count, nullptr); +} + +VPAPI_ATTR VkResult vpCreateDevice(VkPhysicalDevice physicalDevice, const VpDeviceCreateInfo *pCreateInfo, + const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) { + if (physicalDevice == VK_NULL_HANDLE || pCreateInfo == nullptr || pDevice == nullptr) { + return vkCreateDevice(physicalDevice, pCreateInfo == nullptr ? nullptr : pCreateInfo->pCreateInfo, pAllocator, pDevice); + } + + const std::vector<VpBlockProperties>& blocks = detail::GatherBlocks( + pCreateInfo->enabledFullProfileCount, pCreateInfo->pEnabledFullProfiles, + pCreateInfo->enabledProfileBlockCount, pCreateInfo->pEnabledProfileBlocks); + + std::unique_ptr<detail::FeaturesChain> chain = std::make_unique<detail::FeaturesChain>(); + std::vector<VkStructureType> structureTypes; + + std::vector<const char*> extensions; + for (std::uint32_t i = 0, n = pCreateInfo->pCreateInfo->enabledExtensionCount; i < n; ++i) { + extensions.push_back(pCreateInfo->pCreateInfo->ppEnabledExtensionNames[i]); + } + + for (std::size_t i = 0, n = blocks.size(); i < n; ++i) { + const detail::VpProfileDesc* pProfileDesc = detail::vpGetProfileDesc(blocks[i].profiles.profileName); + if (pProfileDesc == nullptr) return VK_ERROR_UNKNOWN; + + for (std::size_t j = 0, p = pProfileDesc->requiredCapabilityCount; j < p; ++j) { + const detail::VpCapabilitiesDesc* pCapsDesc = &pProfileDesc->pRequiredCapabilities[j]; + + for (std::size_t v = 0, q = pCapsDesc->variantCount; v < q; ++v) { + const detail::VpVariantDesc* variant = &pCapsDesc->pVariants[v]; + + if (strcmp(blocks[i].blockName, "") != 0) { + if (strcmp(variant->blockName, blocks[i].blockName) != 0) { + continue; + } + } + + for (uint32_t t = 0; t < variant->featureStructTypeCount; ++t) { + const VkStructureType type = variant->pFeatureStructTypes[t]; + if (std::find(structureTypes.begin(), structureTypes.end(), type) == std::end(structureTypes)) { + structureTypes.push_back(type); + } + } + + detail::GetExtensions(variant->deviceExtensionCount, variant->pDeviceExtensions, extensions); + } + } + } + + VkBaseOutStructure* pNext = static_cast<VkBaseOutStructure*>(const_cast<void*>(pCreateInfo->pCreateInfo->pNext)); + detail::GatherStructureTypes(structureTypes, pNext); + + chain->Build(structureTypes); + + VkPhysicalDeviceFeatures2KHR* pFeatures = &chain->requiredFeaturesChain; + if (pCreateInfo->pCreateInfo->pEnabledFeatures) { + pFeatures->features = *pCreateInfo->pCreateInfo->pEnabledFeatures; + } + + for (std::size_t i = 0, n = blocks.size(); i < n; ++i) { + const detail::VpProfileDesc* pProfileDesc = detail::vpGetProfileDesc(blocks[i].profiles.profileName); + if (pProfileDesc == nullptr) return VK_ERROR_UNKNOWN; + + for (std::size_t j = 0, p = pProfileDesc->requiredCapabilityCount; j < p; ++j) { + const detail::VpCapabilitiesDesc* pCapsDesc = &pProfileDesc->pRequiredCapabilities[j]; + + for (std::size_t v = 0, q = pCapsDesc->variantCount; v < q; ++v) { + const detail::VpVariantDesc* variant = &pCapsDesc->pVariants[v]; + + VkBaseOutStructure* p = reinterpret_cast<VkBaseOutStructure*>(pFeatures); + if (variant->feature.pfnFiller != nullptr) { + while (p != nullptr) { + variant->feature.pfnFiller(p); + p = p->pNext; + } + } + } + } + } + + chain->ApplyFeatures(pCreateInfo); + + if (pCreateInfo->flags & VP_DEVICE_CREATE_DISABLE_ROBUST_BUFFER_ACCESS_BIT) { + pFeatures->features.robustBufferAccess = VK_FALSE; + } + + VkDeviceCreateInfo createInfo{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO}; + createInfo.pNext = &chain->requiredFeaturesChain; + createInfo.queueCreateInfoCount = pCreateInfo->pCreateInfo->queueCreateInfoCount; + createInfo.pQueueCreateInfos = pCreateInfo->pCreateInfo->pQueueCreateInfos; + createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + return vkCreateDevice(physicalDevice, &createInfo, pAllocator, pDevice); +} + +VPAPI_ATTR VkResult vpGetProfileInstanceExtensionProperties(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) { + return detail::vpGetProfileExtensionProperties(pProfile, pBlockName, detail::EXTENSION_INSTANCE, pPropertyCount, pProperties); +} + +VPAPI_ATTR VkResult vpGetProfileDeviceExtensionProperties(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) { + return detail::vpGetProfileExtensionProperties(pProfile, pBlockName, detail::EXTENSION_DEVICE, pPropertyCount, pProperties); +} + +VPAPI_ATTR VkResult vpGetProfileFeatures(const VpProfileProperties *pProfile, const char* pBlockName, void *pNext) { + VkResult result = pBlockName == nullptr ? VK_SUCCESS : VK_INCOMPLETE; + + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile); + + for (std::size_t profile_index = 0, profile_count = profiles.size(); profile_index < profile_count; ++profile_index) { + const detail::VpProfileDesc* profile_desc = detail::vpGetProfileDesc(profiles[profile_index].profileName); + if (profile_desc == nullptr) return VK_ERROR_UNKNOWN; + + for (uint32_t capability_index = 0; capability_index < profile_desc->requiredCapabilityCount; ++capability_index) { + const detail::VpCapabilitiesDesc& capabilities = profile_desc->pRequiredCapabilities[capability_index]; + + for (uint32_t variant_index = 0; variant_index < capabilities.variantCount; ++variant_index) { + const detail::VpVariantDesc& variant = capabilities.pVariants[variant_index]; + if (pBlockName != nullptr) { + if (strcmp(variant.blockName, pBlockName) != 0) { + continue; + } + result = VK_SUCCESS; + } + + if (variant.feature.pfnFiller == nullptr) continue; + + VkBaseOutStructure* p = static_cast<VkBaseOutStructure*>(pNext); + while (p != nullptr) { + variant.feature.pfnFiller(p); + p = p->pNext; + } + } + } + } + + return result; +} + +VPAPI_ATTR VkResult vpGetProfileProperties(const VpProfileProperties *pProfile, const char* pBlockName, void *pNext) { + VkResult result = pBlockName == nullptr ? VK_SUCCESS : VK_INCOMPLETE; + + VkBool32 multiple_variants = VK_FALSE; + if (vpHasMultipleVariantsProfile(pProfile, &multiple_variants) == VK_ERROR_UNKNOWN) { + return VK_ERROR_UNKNOWN; + } + if (multiple_variants == VK_TRUE && pBlockName == nullptr) { + return VK_ERROR_UNKNOWN; + } + + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile); + + for (std::size_t profile_index = 0, profile_count = profiles.size(); profile_index < profile_count; ++profile_index) { + const detail::VpProfileDesc* profile_desc = detail::vpGetProfileDesc(profiles[profile_index].profileName); + if (profile_desc == nullptr) return VK_ERROR_UNKNOWN; + + for (uint32_t capability_index = 0; capability_index < profile_desc->requiredCapabilityCount; ++capability_index) { + const detail::VpCapabilitiesDesc& capabilities = profile_desc->pRequiredCapabilities[capability_index]; + + for (uint32_t variant_index = 0; variant_index < capabilities.variantCount; ++variant_index) { + const detail::VpVariantDesc& variant = capabilities.pVariants[variant_index]; + if (pBlockName != nullptr) { + if (strcmp(variant.blockName, pBlockName) != 0) { + continue; + } + result = VK_SUCCESS; + } + + if (variant.property.pfnFiller == nullptr) continue; + + VkBaseOutStructure* p = static_cast<VkBaseOutStructure*>(pNext); + while (p != nullptr) { + variant.property.pfnFiller(p); + p = p->pNext; + } + } + } + } + + return result; +} + +VPAPI_ATTR VkResult vpGetProfileFormats(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pFormatCount, VkFormat *pFormats) { + VkResult result = pBlockName == nullptr ? VK_SUCCESS : VK_INCOMPLETE; + + std::vector<VkFormat> results; + + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile); + + for (std::size_t profile_index = 0, profile_count = profiles.size(); profile_index < profile_count; ++profile_index) { + const detail::VpProfileDesc* profile_desc = detail::vpGetProfileDesc(profiles[profile_index].profileName); + if (profile_desc == nullptr) return VK_ERROR_UNKNOWN; + + for (uint32_t capability_index = 0; capability_index < profile_desc->requiredCapabilityCount; ++capability_index) { + const detail::VpCapabilitiesDesc& capabilities = profile_desc->pRequiredCapabilities[capability_index]; + + for (uint32_t variant_index = 0; variant_index < capabilities.variantCount; ++variant_index) { + const detail::VpVariantDesc& variant = capabilities.pVariants[variant_index]; + if (pBlockName != nullptr) { + if (strcmp(variant.blockName, pBlockName) != 0) { + continue; + } + result = VK_SUCCESS; + } + + for (uint32_t i = 0; i < variant.formatCount; ++i) { + if (std::find(results.begin(), results.end(), variant.pFormats[i].format) == std::end(results)) { + results.push_back(variant.pFormats[i].format); + } + } + } + } + } + + const uint32_t count = static_cast<uint32_t>(results.size()); + + if (pFormats == nullptr) { + *pFormatCount = count; + } else { + if (*pFormatCount < count) { + result = VK_INCOMPLETE; + } else { + *pFormatCount = count; + } + + if (*pFormatCount > 0) { + memcpy(pFormats, &results[0], *pFormatCount * sizeof(VkFormat)); + } + } + return result; +} + +VPAPI_ATTR VkResult vpGetProfileFormatProperties(const VpProfileProperties *pProfile, const char* pBlockName, VkFormat format, void *pNext) { + VkResult result = pBlockName == nullptr ? VK_SUCCESS : VK_INCOMPLETE; + + const std::vector<VpProfileProperties>& profiles = detail::GatherProfiles(*pProfile); + + for (std::size_t i = 0, n = profiles.size(); i < n; ++i) { + const char* profile_name = profiles[i].profileName; + + const detail::VpProfileDesc* pProfileDesc = detail::vpGetProfileDesc(profile_name); + if (pProfileDesc == nullptr) return VK_ERROR_UNKNOWN; + + for (uint32_t required_capability_index = 0; required_capability_index < pProfileDesc->requiredCapabilityCount; + ++required_capability_index) { + const detail::VpCapabilitiesDesc& required_capabilities = pProfileDesc->pRequiredCapabilities[required_capability_index]; + + for (uint32_t required_variant_index = 0; required_variant_index < required_capabilities.variantCount; ++required_variant_index) { + const detail::VpVariantDesc& variant = required_capabilities.pVariants[required_variant_index]; + if (pBlockName != nullptr) { + if (strcmp(variant.blockName, pBlockName) != 0) { + continue; + } + result = VK_SUCCESS; + } + + for (uint32_t i = 0; i < variant.formatCount; ++i) { + if (variant.pFormats[i].format != format) { + continue; + } + + VkBaseOutStructure* p = static_cast<VkBaseOutStructure*>(static_cast<void*>(pNext)); + while (p != nullptr) { + variant.pFormats[i].pfnFiller(p); + p = p->pNext; + } +#if defined(VK_VERSION_1_3) || defined(VK_KHR_format_feature_flags2) + VkFormatProperties2KHR* fp2 = static_cast<VkFormatProperties2KHR*>( + detail::vpGetStructure(pNext, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR)); + VkFormatProperties3KHR* fp3 = static_cast<VkFormatProperties3KHR*>( + detail::vpGetStructure(pNext, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR)); + if (fp3 != nullptr) { + VkFormatProperties2KHR fp{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR }; + variant.pFormats[i].pfnFiller(static_cast<VkBaseOutStructure*>(static_cast<void*>(&fp))); + fp3->linearTilingFeatures |= static_cast<VkFormatFeatureFlags2KHR>(fp3->linearTilingFeatures | fp.formatProperties.linearTilingFeatures); + fp3->optimalTilingFeatures |= static_cast<VkFormatFeatureFlags2KHR>(fp3->optimalTilingFeatures | fp.formatProperties.optimalTilingFeatures); + fp3->bufferFeatures |= static_cast<VkFormatFeatureFlags2KHR>(fp3->bufferFeatures | fp.formatProperties.bufferFeatures); + } + if (fp2 != nullptr) { + VkFormatProperties3KHR fp{ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR }; + variant.pFormats[i].pfnFiller(static_cast<VkBaseOutStructure*>(static_cast<void*>(&fp))); + fp2->formatProperties.linearTilingFeatures |= static_cast<VkFormatFeatureFlags>(fp2->formatProperties.linearTilingFeatures | fp.linearTilingFeatures); + fp2->formatProperties.optimalTilingFeatures |= static_cast<VkFormatFeatureFlags>(fp2->formatProperties.optimalTilingFeatures | fp.optimalTilingFeatures); + fp2->formatProperties.bufferFeatures |= static_cast<VkFormatFeatureFlags>(fp2->formatProperties.bufferFeatures | fp.bufferFeatures); + } +#endif + } + } + } + } + + return result; +} + +VPAPI_ATTR VkResult vpGetProfileFeatureStructureTypes(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pStructureTypeCount, VkStructureType *pStructureTypes) { + return detail::vpGetProfileStructureTypes(pProfile, pBlockName, detail::STRUCTURE_FEATURE, pStructureTypeCount, pStructureTypes); +} + +VPAPI_ATTR VkResult vpGetProfilePropertyStructureTypes(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pStructureTypeCount, VkStructureType *pStructureTypes) { + return detail::vpGetProfileStructureTypes(pProfile, pBlockName, detail::STRUCTURE_PROPERTY, pStructureTypeCount, pStructureTypes); +} + +VPAPI_ATTR VkResult vpGetProfileFormatStructureTypes(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pStructureTypeCount, VkStructureType *pStructureTypes) { + return detail::vpGetProfileStructureTypes(pProfile, pBlockName, detail::STRUCTURE_FORMAT, pStructureTypeCount, pStructureTypes); +} + +// clang-format on diff --git a/vulkan/vkprofiles/generated/vulkan_profiles.h b/vulkan/vkprofiles/generated/vulkan_profiles.h new file mode 100644 index 0000000000..4bfb6f6945 --- /dev/null +++ b/vulkan/vkprofiles/generated/vulkan_profiles.h @@ -0,0 +1,263 @@ + +/* + * 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. + * +*/ + +// clang-format off + +#ifndef VULKAN_PROFILES_H_ +#define VULKAN_PROFILES_H_ 1 + +#define VPAPI_ATTR + +#ifdef __cplusplus + extern "C" { +#endif + +#include <vulkan/vulkan.h> + +#if defined(VK_VERSION_1_1) && \ + defined(VK_ANDROID_external_memory_android_hardware_buffer) && \ + defined(VK_EXT_queue_family_foreign) && \ + defined(VK_EXT_swapchain_colorspace) && \ + defined(VK_GOOGLE_display_timing) && \ + defined(VK_KHR_android_surface) && \ + defined(VK_KHR_create_renderpass2) && \ + defined(VK_KHR_dedicated_allocation) && \ + defined(VK_KHR_descriptor_update_template) && \ + defined(VK_KHR_driver_properties) && \ + defined(VK_KHR_external_fence) && \ + defined(VK_KHR_external_fence_capabilities) && \ + defined(VK_KHR_external_fence_fd) && \ + defined(VK_KHR_external_memory) && \ + defined(VK_KHR_external_memory_capabilities) && \ + defined(VK_KHR_external_semaphore) && \ + defined(VK_KHR_external_semaphore_capabilities) && \ + defined(VK_KHR_external_semaphore_fd) && \ + defined(VK_KHR_get_memory_requirements2) && \ + defined(VK_KHR_get_physical_device_properties2) && \ + defined(VK_KHR_get_surface_capabilities2) && \ + defined(VK_KHR_incremental_present) && \ + defined(VK_KHR_maintenance1) && \ + defined(VK_KHR_sampler_mirror_clamp_to_edge) && \ + defined(VK_KHR_storage_buffer_storage_class) && \ + defined(VK_KHR_surface) && \ + defined(VK_KHR_swapchain) && \ + defined(VK_KHR_variable_pointers) +#define VP_ANDROID_baseline_2022 1 +#define VP_ANDROID_BASELINE_2022_NAME "VP_ANDROID_baseline_2022" +#define VP_ANDROID_BASELINE_2022_SPEC_VERSION 1 +#define VP_ANDROID_BASELINE_2022_MIN_API_VERSION VK_MAKE_VERSION(1, 1, 106) +#endif + +#if defined(VK_VERSION_1_3) && \ + defined(VP_ANDROID_baseline_2022) && \ + defined(VK_ANDROID_external_format_resolve) && \ + defined(VK_EXT_4444_formats) && \ + defined(VK_EXT_custom_border_color) && \ + defined(VK_EXT_device_memory_report) && \ + defined(VK_EXT_external_memory_acquire_unmodified) && \ + defined(VK_EXT_index_type_uint8) && \ + defined(VK_EXT_line_rasterization) && \ + defined(VK_EXT_load_store_op_none) && \ + defined(VK_EXT_primitive_topology_list_restart) && \ + defined(VK_EXT_primitives_generated_query) && \ + defined(VK_EXT_provoking_vertex) && \ + defined(VK_EXT_scalar_block_layout) && \ + defined(VK_EXT_surface_maintenance1) && \ + defined(VK_EXT_swapchain_maintenance1) && \ + defined(VK_GOOGLE_surfaceless_query) && \ + defined(VK_IMG_relaxed_line_rasterization) && \ + defined(VK_KHR_16bit_storage) && \ + defined(VK_KHR_maintenance5) && \ + defined(VK_KHR_shader_float16_int8) && \ + defined(VK_KHR_vertex_attribute_divisor) +#define VP_ANDROID_15_minimums 1 +#define VP_ANDROID_15_MINIMUMS_NAME "VP_ANDROID_15_minimums" +#define VP_ANDROID_15_MINIMUMS_SPEC_VERSION 1 +#define VP_ANDROID_15_MINIMUMS_MIN_API_VERSION VK_MAKE_VERSION(1, 3, 273) +#endif + +#if defined(VK_VERSION_1_0) && \ + defined(VK_EXT_swapchain_colorspace) && \ + defined(VK_GOOGLE_display_timing) && \ + defined(VK_KHR_android_surface) && \ + defined(VK_KHR_dedicated_allocation) && \ + defined(VK_KHR_descriptor_update_template) && \ + defined(VK_KHR_external_fence) && \ + defined(VK_KHR_external_fence_capabilities) && \ + defined(VK_KHR_external_fence_fd) && \ + defined(VK_KHR_external_memory) && \ + defined(VK_KHR_external_memory_capabilities) && \ + defined(VK_KHR_external_semaphore) && \ + defined(VK_KHR_external_semaphore_capabilities) && \ + defined(VK_KHR_external_semaphore_fd) && \ + defined(VK_KHR_get_memory_requirements2) && \ + defined(VK_KHR_get_physical_device_properties2) && \ + defined(VK_KHR_get_surface_capabilities2) && \ + defined(VK_KHR_incremental_present) && \ + defined(VK_KHR_maintenance1) && \ + defined(VK_KHR_storage_buffer_storage_class) && \ + defined(VK_KHR_surface) && \ + defined(VK_KHR_swapchain) && \ + defined(VK_KHR_variable_pointers) +#define VP_ANDROID_baseline_2021 1 +#define VP_ANDROID_BASELINE_2021_NAME "VP_ANDROID_baseline_2021" +#define VP_ANDROID_BASELINE_2021_SPEC_VERSION 2 +#define VP_ANDROID_BASELINE_2021_MIN_API_VERSION VK_MAKE_VERSION(1, 0, 68) +#endif + +#if defined(VK_VERSION_1_0) && \ + defined(VK_EXT_swapchain_colorspace) && \ + defined(VK_KHR_android_surface) && \ + defined(VK_KHR_dedicated_allocation) && \ + defined(VK_KHR_descriptor_update_template) && \ + defined(VK_KHR_external_fence) && \ + defined(VK_KHR_external_fence_capabilities) && \ + defined(VK_KHR_external_memory) && \ + defined(VK_KHR_external_memory_capabilities) && \ + defined(VK_KHR_external_semaphore) && \ + defined(VK_KHR_external_semaphore_capabilities) && \ + defined(VK_KHR_external_semaphore_fd) && \ + defined(VK_KHR_get_memory_requirements2) && \ + defined(VK_KHR_get_physical_device_properties2) && \ + defined(VK_KHR_get_surface_capabilities2) && \ + defined(VK_KHR_incremental_present) && \ + defined(VK_KHR_maintenance1) && \ + defined(VK_KHR_storage_buffer_storage_class) && \ + defined(VK_KHR_surface) && \ + defined(VK_KHR_swapchain) +#define VP_ANDROID_baseline_2021_cpu_only 1 +#define VP_ANDROID_BASELINE_2021_CPU_ONLY_NAME "VP_ANDROID_baseline_2021_cpu_only" +#define VP_ANDROID_BASELINE_2021_CPU_ONLY_SPEC_VERSION 1 +#define VP_ANDROID_BASELINE_2021_CPU_ONLY_MIN_API_VERSION VK_MAKE_VERSION(1, 0, 68) +#endif + +#define VP_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 2, 0, VK_HEADER_VERSION) + +#define VP_MAX_PROFILE_NAME_SIZE 256U + +typedef struct VpProfileProperties { + char profileName[VP_MAX_PROFILE_NAME_SIZE]; + uint32_t specVersion; +} VpProfileProperties; + +typedef struct VpBlockProperties { + VpProfileProperties profiles; + uint32_t apiVersion; + char blockName[VP_MAX_PROFILE_NAME_SIZE]; +} VpBlockProperties; + +typedef enum VpInstanceCreateFlagBits { + VP_INSTANCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VpInstanceCreateFlagBits; +typedef VkFlags VpInstanceCreateFlags; + +typedef struct VpInstanceCreateInfo { + const VkInstanceCreateInfo* pCreateInfo; + VpInstanceCreateFlags flags; + uint32_t enabledFullProfileCount; + const VpProfileProperties* pEnabledFullProfiles; + uint32_t enabledProfileBlockCount; + const VpBlockProperties* pEnabledProfileBlocks; +} VpInstanceCreateInfo; + +typedef enum VpDeviceCreateFlagBits { + VP_DEVICE_CREATE_DISABLE_ROBUST_BUFFER_ACCESS_BIT = 0x0000001, + VP_DEVICE_CREATE_DISABLE_ROBUST_IMAGE_ACCESS_BIT = 0x0000002, + VP_DEVICE_CREATE_DISABLE_ROBUST_ACCESS = + VP_DEVICE_CREATE_DISABLE_ROBUST_BUFFER_ACCESS_BIT | VP_DEVICE_CREATE_DISABLE_ROBUST_IMAGE_ACCESS_BIT, + + VP_DEVICE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VpDeviceCreateFlagBits; +typedef VkFlags VpDeviceCreateFlags; + +typedef struct VpDeviceCreateInfo { + const VkDeviceCreateInfo* pCreateInfo; + VpDeviceCreateFlags flags; + uint32_t enabledFullProfileCount; + const VpProfileProperties* pEnabledFullProfiles; + uint32_t enabledProfileBlockCount; + const VpBlockProperties* pEnabledProfileBlocks; +} VpDeviceCreateInfo; + +// Query the list of available profiles in the library +VPAPI_ATTR VkResult vpGetProfiles(uint32_t *pPropertyCount, VpProfileProperties *pProperties); + +// List the required profiles of a profile +VPAPI_ATTR VkResult vpGetProfileRequiredProfiles(const VpProfileProperties* pProfile, uint32_t* pPropertyCount, VpProfileProperties* pProperties); + +// Query the profile required Vulkan API version +VPAPI_ATTR uint32_t vpGetProfileAPIVersion(const VpProfileProperties* pProfile); + +// List the recommended fallback profiles of a profile +VPAPI_ATTR VkResult vpGetProfileFallbacks(const VpProfileProperties *pProfile, uint32_t *pPropertyCount, VpProfileProperties *pProperties); + +// Query whether the profile has multiple variants. Profiles with multiple variants can only use vpGetInstanceProfileSupport and vpGetPhysicalDeviceProfileSupport capabilities of the library. Other function will return a VK_ERROR_UNKNOWN error +VPAPI_ATTR VkResult vpHasMultipleVariantsProfile(const VpProfileProperties *pProfile, VkBool32 *pHasMultipleVariants); + +// Check whether a profile is supported at the instance level +VPAPI_ATTR VkResult vpGetInstanceProfileSupport(const char *pLayerName, const VpProfileProperties *pProfile, VkBool32 *pSupported); + +// Check whether a variant of a profile is supported at the instance level and report this list of blocks used to validate the profiles +VPAPI_ATTR VkResult vpGetInstanceProfileVariantsSupport(const char *pLayerName, const VpProfileProperties *pProfile, VkBool32 *pSupported, uint32_t *pPropertyCount, VpBlockProperties* pProperties); + +// Create a VkInstance with the profile instance extensions enabled +VPAPI_ATTR VkResult vpCreateInstance(const VpInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance); + +// Check whether a profile is supported by the physical device +VPAPI_ATTR VkResult vpGetPhysicalDeviceProfileSupport(VkInstance instance, VkPhysicalDevice physicalDevice, const VpProfileProperties *pProfile, VkBool32 *pSupported); + +// Check whether a variant of a profile is supported by the physical device and report this list of blocks used to validate the profiles +VPAPI_ATTR VkResult vpGetPhysicalDeviceProfileVariantsSupport(VkInstance instance, VkPhysicalDevice physicalDevice, const VpProfileProperties *pProfile, VkBool32 *pSupported, uint32_t *pPropertyCount, VpBlockProperties* pProperties); + +// Create a VkDevice with the profile features and device extensions enabled +VPAPI_ATTR VkResult vpCreateDevice(VkPhysicalDevice physicalDevice, const VpDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice); + +// Query the list of instance extensions of a profile +VPAPI_ATTR VkResult vpGetProfileInstanceExtensionProperties(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties); + +// Query the list of device extensions of a profile +VPAPI_ATTR VkResult vpGetProfileDeviceExtensionProperties(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties); + +// Fill the feature structures with the requirements of a profile +VPAPI_ATTR VkResult vpGetProfileFeatures(const VpProfileProperties *pProfile, const char* pBlockName, void *pNext); + +// Query the list of feature structure types specified by the profile +VPAPI_ATTR VkResult vpGetProfileFeatureStructureTypes(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pStructureTypeCount, VkStructureType *pStructureTypes); + +// Fill the property structures with the requirements of a profile +VPAPI_ATTR VkResult vpGetProfileProperties(const VpProfileProperties *pProfile, const char* pBlockName, void *pNext); + +// Query the list of property structure types specified by the profile +VPAPI_ATTR VkResult vpGetProfilePropertyStructureTypes(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pStructureTypeCount, VkStructureType *pStructureTypes); + +// Query the list of formats with specified requirements by a profile +VPAPI_ATTR VkResult vpGetProfileFormats(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pFormatCount, VkFormat *pFormats); + +// Query the requirements of a format for a profile +VPAPI_ATTR VkResult vpGetProfileFormatProperties(const VpProfileProperties *pProfile, const char* pBlockName, VkFormat format, void *pNext); + +// Query the list of format structure types specified by the profile +VPAPI_ATTR VkResult vpGetProfileFormatStructureTypes(const VpProfileProperties *pProfile, const char* pBlockName, uint32_t *pStructureTypeCount, VkStructureType *pStructureTypes); + +#ifdef __cplusplus +} +#endif + +#endif // VULKAN_PROFILES_H_ + +// clang-format on diff --git a/vulkan/vkprofiles/profiles/VP_ANDROID_15_minimums.json b/vulkan/vkprofiles/profiles/VP_ANDROID_15_minimums.json new file mode 100644 index 0000000000..9e17253d75 --- /dev/null +++ b/vulkan/vkprofiles/profiles/VP_ANDROID_15_minimums.json @@ -0,0 +1,174 @@ +{ + "$schema": "https://schema.khronos.org/vulkan/profiles-0.8.2-273.json#", + "capabilities": { + "MUST": { + "extensions": { + "VK_KHR_maintenance5": 1, + "VK_KHR_shader_float16_int8": 1, + "VK_KHR_16bit_storage": 1, + "VK_KHR_vertex_attribute_divisor": 1, + "VK_EXT_custom_border_color": 1, + "VK_EXT_device_memory_report": 1, + "VK_EXT_external_memory_acquire_unmodified": 1, + "VK_EXT_index_type_uint8": 1, + "VK_EXT_load_store_op_none": 1, + "VK_EXT_primitive_topology_list_restart": 1, + "VK_EXT_provoking_vertex": 1, + "VK_EXT_scalar_block_layout": 1, + "VK_EXT_surface_maintenance1": 1, + "VK_EXT_swapchain_maintenance1": 1, + "VK_EXT_4444_formats": 1, + "VK_ANDROID_external_format_resolve": 1, + "VK_GOOGLE_surfaceless_query": 1 + }, + "features": { + "VkPhysicalDeviceFeatures": { + "drawIndirectFirstInstance": true, + "shaderImageGatherExtended": true, + "shaderStorageImageExtendedFormats": true, + "shaderStorageImageReadWithoutFormat": true, + "shaderStorageImageWriteWithoutFormat": true, + "samplerAnisotropy": true + }, + "VkPhysicalDeviceVulkan12Features": { + "shaderFloat16": true, + "shaderInt8": true + }, + "VkPhysicalDeviceCustomBorderColorFeaturesEXT": { + "customBorderColors": true + }, + "VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT": { + "primitiveTopologyListRestart": true + }, + "VkPhysicalDeviceProvokingVertexFeaturesEXT": { + "provokingVertexLast": true + }, + "VkPhysicalDeviceIndexTypeUint8FeaturesEXT": { + "indexTypeUint8": true + }, + "VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR": { + "vertexAttributeInstanceRateDivisor": true + }, + "VkPhysicalDeviceSamplerYcbcrConversionFeatures": { + "samplerYcbcrConversion": true + }, + "VkPhysicalDeviceShaderFloat16Int8Features": { + "shaderFloat16": true, + "shaderInt8": true + }, + "VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures": { + "shaderSubgroupExtendedTypes": true + }, + "VkPhysicalDevice8BitStorageFeatures": { + "storageBuffer8BitAccess": true + }, + "VkPhysicalDevice16BitStorageFeatures": { + "storageBuffer16BitAccess": true + } + }, + "properties": { + "VkPhysicalDeviceProperties": { + "limits": { + "maxPerStageDescriptorUniformBuffers": 13, + "maxPerStageDescriptorStorageBuffers": 12, + "maxColorAttachments": 8, + "maxPerStageDescriptorSampledImages": 128, + "maxPerStageDescriptorSamplers": 128 + } + }, + "VkPhysicalDeviceVulkan11Properties": { + "subgroupSupportedOperations": ["VK_SUBGROUP_FEATURE_BASIC_BIT", "VK_SUBGROUP_FEATURE_VOTE_BIT", "VK_SUBGROUP_FEATURE_ARITHMETIC_BIT", "VK_SUBGROUP_FEATURE_BALLOT_BIT", "VK_SUBGROUP_FEATURE_SHUFFLE_BIT", "VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT"] + } + }, + "formats": { + "VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + } + } + }, + "primitivesGeneratedQuery": { + "extensions": { + "VK_EXT_primitives_generated_query": 1 + }, + "features": { + "VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT": { + "primitivesGeneratedQuery": true + } + } + }, + "pipelineStatisticsQuery": { + "features": { + "VkPhysicalDeviceFeatures": { + "pipelineStatisticsQuery": true + } + } + }, + "swBresenhamLines": { + "extensions": { + "VK_EXT_line_rasterization": 1 + }, + "features": { + "VkPhysicalDeviceLineRasterizationFeaturesEXT": { + "bresenhamLines": true + } + } + }, + "hwBresenhamLines": { + "extensions": { + "VK_IMG_relaxed_line_rasterization": 1 + }, + "features": { + "VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG": { + "relaxedLineRasterization": true + } + } + } + }, + "profiles": { + "VP_ANDROID_15_minimums": { + "version": 1, + "api-version": "1.3.273", + "label": "Vulkan Minimum Requirements for Android 15", + "description": "Collection of functionality that is mandated for chipsets that launch (or renew Google Requirements Freeze) on Android 15", + "contributors": { + "Trevor David Black": { + "company": "Google", + "email": "vantablack@google.com", + "contact": true + }, + "Ian Elliott": { + "company": "Google", + "email": "ianelliott@google.com", + "contact": true + } + }, + "history": [ + { + "revision": 1, + "date": "2023-12-15", + "author": "Ian Elliott", + "comment": "First version" + } + ], + "profiles": [ + "VP_ANDROID_baseline_2022" + ], + "capabilities": [ + "MUST", + ["primitivesGeneratedQuery", "pipelineStatisticsQuery"], + ["swBresenhamLines", "hwBresenhamLines"] + ] + } + } +} diff --git a/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2021.json b/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2021.json new file mode 100644 index 0000000000..1a437fba2c --- /dev/null +++ b/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2021.json @@ -0,0 +1,794 @@ +{ + "$schema": "https://schema.khronos.org/vulkan/profiles-0.8.0-106.json#", + "capabilities": { + "baseline": { + "extensions": { + "VK_KHR_surface": 1, + "VK_KHR_android_surface": 1, + "VK_KHR_swapchain": 1, + "VK_KHR_get_physical_device_properties2": 1, + "VK_KHR_maintenance1": 1, + "VK_EXT_swapchain_colorspace": 1, + "VK_KHR_get_surface_capabilities2": 1, + "VK_KHR_incremental_present": 1, + "VK_GOOGLE_display_timing": 1, + "VK_KHR_descriptor_update_template": 1, + "VK_KHR_get_memory_requirements2": 1, + "VK_KHR_dedicated_allocation": 1, + "VK_KHR_storage_buffer_storage_class": 1, + "VK_KHR_external_semaphore_capabilities": 1, + "VK_KHR_external_semaphore": 1, + "VK_KHR_external_memory_capabilities": 1, + "VK_KHR_external_memory": 1, + "VK_KHR_external_fence_capabilities": 1, + "VK_KHR_external_semaphore_fd": 1, + "VK_KHR_external_fence": 1, + "VK_KHR_external_fence_fd": 1, + "VK_KHR_variable_pointers": 1 + }, + "features": { + "VkPhysicalDeviceFeatures": { + "depthBiasClamp": true, + "fragmentStoresAndAtomics": true, + "fullDrawIndexUint32": true, + "imageCubeArray": true, + "independentBlend": true, + "robustBufferAccess": true, + "sampleRateShading": true, + "shaderSampledImageArrayDynamicIndexing": true, + "shaderStorageImageArrayDynamicIndexing": true, + "shaderUniformBufferArrayDynamicIndexing": true, + "textureCompressionASTC_LDR": true, + "textureCompressionETC2": true + } + }, + "properties": { + "VkPhysicalDeviceProperties": { + "limits": { + "maxImageDimension1D": 4096, + "maxImageDimension2D": 4096, + "maxImageDimension3D": 512, + "maxImageDimensionCube": 4096, + "maxImageArrayLayers": 256, + "maxTexelBufferElements": 65536, + "maxUniformBufferRange": 16384, + "maxStorageBufferRange": 134217728, + "maxPushConstantsSize": 128, + "maxMemoryAllocationCount": 4096, + "maxSamplerAllocationCount": 4000, + "maxBoundDescriptorSets": 4, + "maxPerStageDescriptorSamplers": 16, + "maxPerStageDescriptorUniformBuffers": 12, + "maxPerStageDescriptorStorageBuffers": 4, + "maxPerStageDescriptorSampledImages": 16, + "maxPerStageDescriptorStorageImages": 4, + "maxPerStageDescriptorInputAttachments": 4, + "maxPerStageResources": 44, + "maxDescriptorSetSamplers": 48, + "maxDescriptorSetUniformBuffers": 36, + "maxDescriptorSetUniformBuffersDynamic": 8, + "maxDescriptorSetStorageBuffers": 24, + "maxDescriptorSetStorageBuffersDynamic": 4, + "maxDescriptorSetSampledImages": 48, + "maxDescriptorSetStorageImages": 12, + "maxDescriptorSetInputAttachments": 4, + "maxVertexInputAttributes": 16, + "maxVertexInputBindings": 16, + "maxVertexInputAttributeOffset": 2047, + "maxVertexInputBindingStride": 2048, + "maxVertexOutputComponents": 64, + "maxFragmentInputComponents": 64, + "maxFragmentOutputAttachments": 4, + "maxFragmentCombinedOutputResources": 8, + "maxComputeSharedMemorySize": 16384, + "maxComputeWorkGroupCount": [ 65535, 65535, 65535 ], + "maxComputeWorkGroupInvocations": 128, + "maxComputeWorkGroupSize": [ 128, 128, 64 ], + "subPixelPrecisionBits": 4, + "subTexelPrecisionBits": 4, + "mipmapPrecisionBits": 4, + "maxDrawIndexedIndexValue": 4294967295, + "maxDrawIndirectCount": 1, + "maxSamplerLodBias": 2.0, + "maxSamplerAnisotropy": 1.0, + "maxViewports": 1, + "maxViewportDimensions": [ 4096, 4096 ], + "viewportBoundsRange": [ -8192, 8191 ], + "minMemoryMapAlignment": 4096, + "minTexelBufferOffsetAlignment": 256, + "minUniformBufferOffsetAlignment": 256, + "minStorageBufferOffsetAlignment": 256, + "minTexelOffset": -8, + "maxTexelOffset": 7, + "minInterpolationOffset": -0.5, + "maxInterpolationOffset": 0.4375, + "subPixelInterpolationOffsetBits": 4, + "maxFramebufferWidth": 4096, + "maxFramebufferHeight": 4096, + "maxFramebufferLayers": 256, + "framebufferColorSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferDepthSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferStencilSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferNoAttachmentsSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "maxColorAttachments": 4, + "sampledImageColorSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "sampledImageIntegerSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT" ], + "sampledImageDepthSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "sampledImageStencilSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "storageImageSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT" ], + "maxSampleMaskWords": 1, + "discreteQueuePriorities": 2, + "pointSizeGranularity": 1, + "standardSampleLocations": true + } + } + }, + "formats": { + "VK_FORMAT_ASTC_4x4_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_4x4_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x4_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x4_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x10_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x10_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x10_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x10_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x12_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x12_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B4G4R4A4_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R5G6B5_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A1R5G5B5_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_SRGB": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B8G8R8A8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_B8G8R8A8_SRGB": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A8B8G8R8_UNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A8B8G8R8_SNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A8B8G8R8_UINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A8B8G8R8_SINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A8B8G8R8_SRGB_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A2B10G10R10_UNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A2B10G10R10_UINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": [ "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": [ "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": [ "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R16G16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16B16A16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": [ "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16B16A16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16B16A16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16B16A16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32G32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32G32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32G32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32G32B32A32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R32G32B32A32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R32G32B32A32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B10G11R11_UFLOAT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT" ] + } + }, + "VK_FORMAT_E5B9G9R9_UFLOAT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_D16_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_D32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11_SNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11G11_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11G11_SNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + } + } + } + }, + "profiles": { + "VP_ANDROID_baseline_2021": { + "version": 2, + "api-version": "1.0.68", + "label": "Android Vulkan Baseline 2021 profile", + "description": "Collection of functionality that is broadly supported on Android", + "contributors": { + "Trevor David Black": { + "company": "Google", + "email": "vantablack@google.com", + "contact": true + }, + "Ian Elliott": { + "company": "Google", + "email": "ianelliott@google.com", + "contact": true + }, + "Frank Yang": { + "company": "Google", + "contact": false + } + }, + "history": [ + { + "revision": 2, + "date": "2023-01-10", + "author": "Trevor David Black", + "comment": "Remove shaderImageGatherExtended and limits" + }, + { + "revision": 1, + "date": "2022-01-11", + "author": "Trevor David Black", + "comment": "Final draft" + } + ], + "capabilities": [ + "baseline" + ] + } + } +} diff --git a/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2021_cpu_only.json b/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2021_cpu_only.json new file mode 100644 index 0000000000..7ce337ca3a --- /dev/null +++ b/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2021_cpu_only.json @@ -0,0 +1,780 @@ +{ + "$schema": "https://schema.khronos.org/vulkan/profiles-0.8.0-106.json#", + "capabilities": { + "baseline": { + "extensions": { + "VK_KHR_surface": 1, + "VK_KHR_android_surface": 1, + "VK_KHR_swapchain": 1, + "VK_KHR_get_physical_device_properties2": 1, + "VK_KHR_maintenance1": 1, + "VK_EXT_swapchain_colorspace": 1, + "VK_KHR_get_surface_capabilities2": 1, + "VK_KHR_incremental_present": 1, + "VK_KHR_descriptor_update_template": 1, + "VK_KHR_get_memory_requirements2": 1, + "VK_KHR_dedicated_allocation": 1, + "VK_KHR_storage_buffer_storage_class": 1, + "VK_KHR_external_semaphore_capabilities": 1, + "VK_KHR_external_semaphore": 1, + "VK_KHR_external_memory_capabilities": 1, + "VK_KHR_external_memory": 1, + "VK_KHR_external_fence_capabilities": 1, + "VK_KHR_external_semaphore_fd": 1, + "VK_KHR_external_fence": 1 + }, + "features": { + "VkPhysicalDeviceFeatures": { + "depthBiasClamp": true, + "fragmentStoresAndAtomics": true, + "fullDrawIndexUint32": true, + "imageCubeArray": true, + "independentBlend": true, + "robustBufferAccess": true, + "sampleRateShading": true, + "shaderSampledImageArrayDynamicIndexing": true, + "shaderStorageImageArrayDynamicIndexing": true, + "shaderUniformBufferArrayDynamicIndexing": true, + "textureCompressionASTC_LDR": true, + "textureCompressionETC2": true + } + }, + "properties": { + "VkPhysicalDeviceProperties": { + "limits": { + "maxImageDimension1D": 4096, + "maxImageDimension2D": 4096, + "maxImageDimension3D": 512, + "maxImageDimensionCube": 4096, + "maxImageArrayLayers": 256, + "maxTexelBufferElements": 65536, + "maxUniformBufferRange": 16384, + "maxStorageBufferRange": 134217728, + "maxPushConstantsSize": 128, + "maxMemoryAllocationCount": 4096, + "maxSamplerAllocationCount": 4000, + "maxBoundDescriptorSets": 4, + "maxPerStageDescriptorSamplers": 16, + "maxPerStageDescriptorUniformBuffers": 12, + "maxPerStageDescriptorStorageBuffers": 4, + "maxPerStageDescriptorSampledImages": 16, + "maxPerStageDescriptorStorageImages": 4, + "maxPerStageDescriptorInputAttachments": 4, + "maxPerStageResources": 44, + "maxDescriptorSetSamplers": 48, + "maxDescriptorSetUniformBuffers": 36, + "maxDescriptorSetUniformBuffersDynamic": 8, + "maxDescriptorSetStorageBuffers": 24, + "maxDescriptorSetStorageBuffersDynamic": 4, + "maxDescriptorSetSampledImages": 48, + "maxDescriptorSetStorageImages": 12, + "maxDescriptorSetInputAttachments": 4, + "maxVertexInputAttributes": 16, + "maxVertexInputBindings": 16, + "maxVertexInputAttributeOffset": 2047, + "maxVertexInputBindingStride": 2048, + "maxVertexOutputComponents": 64, + "maxFragmentInputComponents": 64, + "maxFragmentOutputAttachments": 4, + "maxFragmentCombinedOutputResources": 8, + "maxComputeSharedMemorySize": 16384, + "maxComputeWorkGroupCount": [ 65535, 65535, 65535 ], + "maxComputeWorkGroupInvocations": 128, + "maxComputeWorkGroupSize": [ 128, 128, 64 ], + "subPixelPrecisionBits": 4, + "subTexelPrecisionBits": 4, + "mipmapPrecisionBits": 4, + "maxDrawIndexedIndexValue": 4294967295, + "maxDrawIndirectCount": 1, + "maxSamplerLodBias": 2.0, + "maxSamplerAnisotropy": 1.0, + "maxViewports": 1, + "maxViewportDimensions": [ 4096, 4096 ], + "viewportBoundsRange": [ -8192, 8191 ], + "minMemoryMapAlignment": 4096, + "minTexelBufferOffsetAlignment": 256, + "minUniformBufferOffsetAlignment": 256, + "minStorageBufferOffsetAlignment": 256, + "minTexelOffset": -8, + "maxTexelOffset": 7, + "minInterpolationOffset": -0.5, + "maxInterpolationOffset": 0.4375, + "subPixelInterpolationOffsetBits": 4, + "maxFramebufferWidth": 4096, + "maxFramebufferHeight": 4096, + "maxFramebufferLayers": 256, + "framebufferColorSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferDepthSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferStencilSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferNoAttachmentsSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "maxColorAttachments": 4, + "sampledImageColorSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "sampledImageIntegerSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT" ], + "sampledImageDepthSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "sampledImageStencilSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "storageImageSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT" ], + "maxSampleMaskWords": 1, + "discreteQueuePriorities": 2, + "standardSampleLocations": true + } + } + }, + "formats": { + "VK_FORMAT_ASTC_4x4_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_4x4_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x4_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x4_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x10_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x10_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x10_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x10_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x12_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x12_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B4G4R4A4_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R5G6B5_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A1R5G5B5_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R8G8B8A8_SRGB": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B8G8R8A8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_B8G8R8A8_SRGB": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A8B8G8R8_UNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A8B8G8R8_SNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A8B8G8R8_UINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A8B8G8R8_SINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A8B8G8R8_SRGB_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A2B10G10R10_UNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_A2B10G10R10_UINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": [ "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": [ "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": [ "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R16G16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16B16A16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": [ "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16B16A16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16B16A16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R16G16B16A16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32G32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32G32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32G32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT" ] + } + }, + "VK_FORMAT_R32G32B32A32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R32G32B32A32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R32G32B32A32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B10G11R11_UFLOAT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [ "VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT" ] + } + }, + "VK_FORMAT_E5B9G9R9_UFLOAT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_D16_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_D32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11_SNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11G11_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11G11_SNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": [ "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "optimalTilingFeatures": [ "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT" ], + "bufferFeatures": [] + } + } + } + } + }, + "profiles": { + "VP_ANDROID_baseline_2021_cpu_only": { + "version": 1, + "api-version": "1.0.68", + "label": "Android Vulkan Baseline 2021 cpu only profile ", + "description": "Collection of functionality that is broadly supported on CPU only Android", + "contributors": { + "Trevor David Black": { + "company": "Google", + "email": "vantablack@google.com", + "contact": true + }, + "Ian Elliott": { + "company": "Google", + "email": "ianelliott@google.com", + "contact": true + } + }, + "history": [ + { + "revision": 1, + "date": "2023-03-01", + "author": "Trevor David Black", + "comment": "Final draft" + } + ], + "capabilities": [ + "baseline" + ] + } + } +} diff --git a/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2022.json b/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2022.json new file mode 100644 index 0000000000..bb3f6a6f60 --- /dev/null +++ b/vulkan/vkprofiles/profiles/VP_ANDROID_baseline_2022.json @@ -0,0 +1,810 @@ +{ + "$schema": "https://schema.khronos.org/vulkan/profiles-0.8.0-106.json#", + "capabilities": { + "baseline": { + "extensions": { + "VK_KHR_surface": 1, + "VK_KHR_android_surface": 1, + "VK_KHR_swapchain": 1, + "VK_KHR_get_physical_device_properties2": 1, + "VK_KHR_maintenance1": 1, + "VK_EXT_swapchain_colorspace": 1, + "VK_KHR_get_surface_capabilities2": 1, + "VK_KHR_incremental_present": 1, + "VK_GOOGLE_display_timing": 1, + "VK_KHR_descriptor_update_template": 1, + "VK_KHR_get_memory_requirements2": 1, + "VK_KHR_dedicated_allocation": 1, + "VK_KHR_storage_buffer_storage_class": 1, + "VK_KHR_external_semaphore_capabilities": 1, + "VK_KHR_external_semaphore": 1, + "VK_KHR_external_memory_capabilities": 1, + "VK_KHR_external_memory": 1, + "VK_KHR_external_fence_capabilities": 1, + "VK_KHR_external_semaphore_fd": 1, + "VK_KHR_external_fence": 1, + "VK_KHR_external_fence_fd": 1, + "VK_KHR_variable_pointers": 1, + "VK_ANDROID_external_memory_android_hardware_buffer": 1, + "VK_EXT_queue_family_foreign": 1, + "VK_KHR_driver_properties": 1, + "VK_KHR_create_renderpass2": 1, + "VK_KHR_sampler_mirror_clamp_to_edge": 1 + }, + "features": { + "VkPhysicalDeviceFeatures": { + "depthBiasClamp": true, + "fragmentStoresAndAtomics": true, + "fullDrawIndexUint32": true, + "imageCubeArray": true, + "independentBlend": true, + "robustBufferAccess": true, + "sampleRateShading": true, + "shaderSampledImageArrayDynamicIndexing": true, + "shaderStorageImageArrayDynamicIndexing": true, + "shaderUniformBufferArrayDynamicIndexing": true, + "textureCompressionASTC_LDR": true, + "textureCompressionETC2": true, + "shaderInt16": true, + "shaderStorageBufferArrayDynamicIndexing": true, + "largePoints": true + }, + "VkPhysicalDeviceMultiviewFeatures": { + "multiview": true + }, + "VkPhysicalDeviceSamplerYcbcrConversionFeatures": { + "samplerYcbcrConversion": true + }, + "VkPhysicalDeviceShaderDrawParametersFeatures": { + "shaderDrawParameters": true + }, + "VkPhysicalDeviceVariablePointersFeatures": { + "variablePointers": true, + "variablePointersStorageBuffer": true + } + }, + "properties": { + "VkPhysicalDeviceProperties": { + "limits": { + "maxImageDimension1D": 4096, + "maxImageDimension2D": 4096, + "maxImageDimension3D": 512, + "maxImageDimensionCube": 4096, + "maxImageArrayLayers": 256, + "maxTexelBufferElements": 65536, + "maxUniformBufferRange": 16384, + "maxStorageBufferRange": 134217728, + "maxPushConstantsSize": 128, + "maxMemoryAllocationCount": 4096, + "maxSamplerAllocationCount": 4000, + "maxBoundDescriptorSets": 4, + "maxPerStageDescriptorSamplers": 16, + "maxPerStageDescriptorUniformBuffers": 12, + "maxPerStageDescriptorStorageBuffers": 4, + "maxPerStageDescriptorSampledImages": 16, + "maxPerStageDescriptorStorageImages": 4, + "maxPerStageDescriptorInputAttachments": 4, + "maxPerStageResources": 44, + "maxDescriptorSetSamplers": 48, + "maxDescriptorSetUniformBuffers": 36, + "maxDescriptorSetUniformBuffersDynamic": 8, + "maxDescriptorSetStorageBuffers": 24, + "maxDescriptorSetStorageBuffersDynamic": 4, + "maxDescriptorSetSampledImages": 48, + "maxDescriptorSetStorageImages": 12, + "maxDescriptorSetInputAttachments": 4, + "maxVertexInputAttributes": 16, + "maxVertexInputBindings": 16, + "maxVertexInputAttributeOffset": 2047, + "maxVertexInputBindingStride": 2048, + "maxVertexOutputComponents": 64, + "maxFragmentInputComponents": 64, + "maxFragmentOutputAttachments": 4, + "maxFragmentCombinedOutputResources": 8, + "maxComputeSharedMemorySize": 16384, + "maxComputeWorkGroupCount": [ 65535, 65535, 65535 ], + "maxComputeWorkGroupInvocations": 128, + "maxComputeWorkGroupSize": [ 128, 128, 64 ], + "subPixelPrecisionBits": 4, + "subTexelPrecisionBits": 4, + "mipmapPrecisionBits": 4, + "maxDrawIndexedIndexValue": 4294967295, + "maxDrawIndirectCount": 1, + "maxSamplerLodBias": 2.0, + "maxSamplerAnisotropy": 1.0, + "maxViewports": 1, + "maxViewportDimensions": [ 4096, 4096 ], + "viewportBoundsRange": [ -8192, 8191 ], + "minMemoryMapAlignment": 4096, + "minTexelBufferOffsetAlignment": 256, + "minUniformBufferOffsetAlignment": 256, + "minStorageBufferOffsetAlignment": 256, + "minTexelOffset": -8, + "maxTexelOffset": 7, + "minInterpolationOffset": -0.5, + "maxInterpolationOffset": 0.4375, + "subPixelInterpolationOffsetBits": 4, + "maxFramebufferWidth": 4096, + "maxFramebufferHeight": 4096, + "maxFramebufferLayers": 256, + "framebufferColorSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferDepthSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferStencilSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "framebufferNoAttachmentsSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "maxColorAttachments": 4, + "sampledImageColorSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "sampledImageIntegerSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT" ], + "sampledImageDepthSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "sampledImageStencilSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT", "VK_SAMPLE_COUNT_4_BIT" ], + "storageImageSampleCounts": [ "VK_SAMPLE_COUNT_1_BIT" ], + "maxSampleMaskWords": 1, + "discreteQueuePriorities": 2, + "pointSizeGranularity": 1, + "pointSizeRange": [1.0, 511], + "standardSampleLocations": true + } + }, + "VkPhysicalDeviceMultiviewProperties": { + "maxMultiviewInstanceIndex": 134217727, + "maxMultiviewViewCount": 6 + } + }, + "formats": { + "VK_FORMAT_ASTC_4x4_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_4x4_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x4_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x4_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_5x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_6x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_8x8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x5_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x5_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x6_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x6_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x10_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_10x10_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x10_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x10_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x12_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ASTC_12x12_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B4G4R4A4_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R5G6B5_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A1R5G5B5_UNORM_PACK16": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8B8A8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8B8A8_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8B8A8_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8B8A8_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R8G8B8A8_SRGB": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B8G8R8A8_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_B8G8R8A8_SRGB": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A8B8G8R8_UNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_A8B8G8R8_SNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_A8B8G8R8_UINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_A8B8G8R8_SINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_A8B8G8R8_SRGB_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_A2B10G10R10_UNORM_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_A2B10G10R10_UINT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": ["VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": ["VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16G16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": ["VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16G16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R16G16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16G16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16G16B16A16_SNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": [], + "bufferFeatures": ["VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16G16B16A16_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16G16B16A16_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R16G16B16A16_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R32G32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R32G32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R32G32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT", "VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT"] + } + }, + "VK_FORMAT_R32G32B32A32_UINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R32G32B32A32_SINT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_R32G32B32A32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT", "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_BLIT_DST_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_B10G11R11_UFLOAT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": ["VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT"] + } + }, + "VK_FORMAT_E5B9G9R9_UFLOAT_PACK32": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_D16_UNORM": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_D32_SFLOAT": { + "VkFormatProperties": { + "linearTilingFeatures": [], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11_SNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11G11_UNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + }, + "VK_FORMAT_EAC_R11G11_SNORM_BLOCK": { + "VkFormatProperties": { + "linearTilingFeatures": ["VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "optimalTilingFeatures": ["VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT", "VK_FORMAT_FEATURE_BLIT_SRC_BIT", "VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT", "VK_FORMAT_FEATURE_TRANSFER_SRC_BIT", "VK_FORMAT_FEATURE_TRANSFER_DST_BIT"], + "bufferFeatures": [] + } + } + } + } + }, + "profiles": { + "VP_ANDROID_baseline_2022": { + "version": 1, + "api-version": "1.1.106", + "label": "Android Vulkan Baseline 2022 profile", + "description": "Collection of functionality that is broadly supported on Android", + "contributors": { + "Trevor David Black": { + "company": "Google", + "email": "vantablack@google.com", + "contact": true + }, + "Ian Elliott": { + "company": "Google", + "email": "ianelliott@google.com", + "contact": true + } + }, + "history": [ + { + "revision": 1, + "date": "2022-12-23", + "author": "Trevor David Black", + "comment": "Final draft" + } + ], + "capabilities": [ + "baseline" + ] + } + } +} diff --git a/vulkan/vkprofiles/vkprofiles.cpp b/vulkan/vkprofiles/vkprofiles.cpp new file mode 100644 index 0000000000..465dc254fd --- /dev/null +++ b/vulkan/vkprofiles/vkprofiles.cpp @@ -0,0 +1,198 @@ +/* + * 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 LOG_TAG "vkprofiles" + +#ifndef VK_USE_PLATFORM_ANDROID_KHR +#define VK_USE_PLATFORM_ANDROID_KHR +#endif + +#include <string> +#include <vector> + +#include <android/log.h> + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) + +#include "generated/vulkan_profiles.h" +#include "vkprofiles.h" + +namespace android::vkprofiles { + +/* Wrap vkProfileGetSupport in an anonymous namespace. + * vkProfileGetSupport only works for profiles that we explicitly add, we don't + * want a user of this library to mistakenly call with a profile that we haven't + * added. + */ +namespace { + +std::string vkProfileGetSupport(const VpProfileProperties* pProfile, + const uint32_t minApiVersion) { + VkResult result = VK_SUCCESS; + VkBool32 supported = VK_FALSE; + + result = vpGetInstanceProfileSupport(nullptr, pProfile, &supported); + if (result != VK_SUCCESS) { + std::string error( + "There was a failure from vpGetInstanceProfileSupport," + " check `vkprofiles` in logcat." + " result = " + + std::to_string(result)); + return error; + } + if (supported != VK_TRUE) { + std::string error( + "There was a failure from vpGetInstanceProfileSupport," + " check `vkprofiles` in logcat." + " supported = " + + std::to_string(supported)); + return error; + } + + const VkApplicationInfo appInfo = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, + nullptr, + "vkprofiles", + 0, + "", + 0, + minApiVersion, + }; + VkInstanceCreateInfo instanceCreateInfo = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + nullptr, + 0, + &appInfo, + 0, + nullptr, + 0, + nullptr, + }; + + VpInstanceCreateInfo vpInstanceCreateInfo{}; + vpInstanceCreateInfo.pCreateInfo = &instanceCreateInfo; + vpInstanceCreateInfo.enabledFullProfileCount = 1; + vpInstanceCreateInfo.pEnabledFullProfiles = pProfile; + + VkInstance instance = VK_NULL_HANDLE; + result = vpCreateInstance(&vpInstanceCreateInfo, nullptr, &instance); + if (result != VK_SUCCESS) { + std::string error( + "There was a failure from vpCreateInstance," + " check `vkprofiles` in logcat." + " result = " + + std::to_string(result)); + return error; + } + + uint32_t count; + result = vkEnumeratePhysicalDevices(instance, &count, nullptr); + if (result != VK_SUCCESS) { + vkDestroyInstance(instance, nullptr); + std::string error( + "There was a failure from vkEnumeratePhysicalDevices," + " check `vkprofiles` in logcat." + " result = " + + std::to_string(result)); + return error; + } + + std::vector<VkPhysicalDevice> devices(count, VK_NULL_HANDLE); + result = vkEnumeratePhysicalDevices(instance, &count, devices.data()); + if (result != VK_SUCCESS) { + vkDestroyInstance(instance, nullptr); + std::string error( + "There was a failure from vkEnumeratePhysicalDevices (2)," + " check `vkprofiles` in logcat." + " result = " + + std::to_string(result)); + return error; + } + + bool onePhysicalDeviceSupports = false; + for (size_t i = 0; i < count; i++) { + result = vpGetPhysicalDeviceProfileSupport(instance, devices[i], + pProfile, &supported); + if (result != VK_SUCCESS) { + ALOGD("vpGetPhysicalDeviceProfileSupport fail, result = %d", + result); + continue; + } else if (supported != VK_TRUE) { + ALOGD("vpGetPhysicalDeviceProfileSupport fail, supported = %d", + supported); + continue; + } + + onePhysicalDeviceSupports = true; + } + + if (!onePhysicalDeviceSupports) { + std::string error( + "There was a failure from vpGetPhysicalDeviceProfileSupport," + " check `vkprofiles` in logcat." + " No VkPhysicalDevice supports the profile"); + return error; + } + + return std::string("SUPPORTED"); +} + +} // anonymous namespace + +std::string vkAbp2021GetSupport() { + VpProfileProperties profile{VP_ANDROID_BASELINE_2021_NAME, + VP_ANDROID_BASELINE_2021_SPEC_VERSION}; + return vkProfileGetSupport(&profile, + VP_ANDROID_BASELINE_2021_MIN_API_VERSION); +} + +std::string vkAbp2021CpuOnlyGetSupport() { + VpProfileProperties profile{VP_ANDROID_BASELINE_2021_CPU_ONLY_NAME, + VP_ANDROID_BASELINE_2021_CPU_ONLY_SPEC_VERSION}; + return vkProfileGetSupport(&profile, + VP_ANDROID_BASELINE_2021_MIN_API_VERSION); +} + +std::string vkAbp2022GetSupport() { + VpProfileProperties profile{VP_ANDROID_BASELINE_2022_NAME, + VP_ANDROID_BASELINE_2022_SPEC_VERSION}; + return vkProfileGetSupport(&profile, + VP_ANDROID_BASELINE_2022_MIN_API_VERSION); +} + +std::string vkVpa15GetSupport() { + VpProfileProperties profile{VP_ANDROID_15_MINIMUMS_NAME, + VP_ANDROID_15_MINIMUMS_SPEC_VERSION}; + return vkProfileGetSupport(&profile, + VP_ANDROID_15_MINIMUMS_MIN_API_VERSION); +} + +std::string vkProfiles() { + return "{" + "\"" + std::string(VP_ANDROID_BASELINE_2021_NAME) + "\": " + "\"" + vkAbp2021GetSupport() + "\"," + "\"" + std::string(VP_ANDROID_BASELINE_2021_CPU_ONLY_NAME) + "\": " + "\"" + vkAbp2021CpuOnlyGetSupport() + "\"," + "\"" + std::string(VP_ANDROID_BASELINE_2022_NAME) + "\": " + "\"" + vkAbp2022GetSupport() + "\"," + "\"" + std::string(VP_ANDROID_15_MINIMUMS_NAME) + "\": " + "\"" + vkVpa15GetSupport() + "\"" + "}"; +} + +} // namespace android::vkprofiles diff --git a/vulkan/vkprofiles/vkprofiles.h b/vulkan/vkprofiles/vkprofiles.h new file mode 100644 index 0000000000..8f42e9bc30 --- /dev/null +++ b/vulkan/vkprofiles/vkprofiles.h @@ -0,0 +1,40 @@ +/* + * 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 <string> + +namespace android::vkprofiles { + +/* + * vk**GetSupport is a function call to determine if the device supports a + * specific Vulkan Profile. These functions call into + * generated/vulkan_profiles.h and so only work with select profiles. If the + * device supports the profile, the string "SUPPORTED" is returned, otherwise an + * error message is returned. + */ +std::string vkAbp2021GetSupport(); +std::string vkAbp2021GetSupportCpuOnly(); +std::string vkAbp2022GetSupport(); +std::string vkVpa15GetSupport(); + +// Returns a json string that enumerates support for any of the Vulkan profiles +// specified in the above functions +std::string vkProfiles(); + +} // namespace android::vkprofiles |