diff options
382 files changed, 14138 insertions, 5815 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index 07d16f7a4d..a4a22a0f1d 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -22,5 +22,21 @@ { "name": "SurfaceFlinger_test" } + ], + "postsubmit": [ + { + "name": "SurfaceFlinger_test", + "keywords": [ "primary-device" ], + "options": [ + // TODO(b/328119950) Known to be broken. + { + "exclude-filter": "LayerCallbackTest#SetNullBuffer" + }, + // TODO(b/398306512) Flaky on real device. + { + "exclude-filter": "LayerRenderTypeTransactionTests/LayerRenderTypeTransactionTest#SetRelativeZBasic_BufferQueue/*" + } + ] + } ] } diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index fdac5db1d1..316f04cb27 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -316,97 +316,6 @@ on late-init && property:ro.boot.fastboot.boottrace= write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0 -# Only create the tracing instance if persist.mm_events.enabled -# Attempting to remove the tracing instance after it has been created -# will likely fail with EBUSY as it would be in use by traced_probes. -on mm_events_property_available && property:persist.mm_events.enabled=true -# Create MM Events Tracing Instance for Kmem Activity Trigger - mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system - mkdir /sys/kernel/tracing/instances/mm_events 0755 system system - -# Read and set per CPU buffer size - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb - chmod 0666 /sys/kernel/tracing/instances/mm_events/buffer_size_kb - -# Set the default buffer size to the minimum - write /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb 1 - write /sys/kernel/tracing/instances/mm_events/buffer_size_kb 1 - -# Read and enable tracing - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/tracing_on - chmod 0666 /sys/kernel/tracing/instances/mm_events/tracing_on - -# Tracing disabled by default - write /sys/kernel/debug/tracing/instances/mm_events/tracing_on 0 - write /sys/kernel/tracing/instances/mm_events/tracing_on 0 - -# Read and truncate kernel trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/trace - -# Enable trace events - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/vmscan/mm_vmscan_direct_reclaim_begin/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/vmscan/mm_vmscan_direct_reclaim_begin/enable - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/vmscan/mm_vmscan_kswapd_wake/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/vmscan/mm_vmscan_kswapd_wake/enable - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/compaction/mm_compaction_begin/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/compaction/mm_compaction_begin/enable - -# Read and clear per-CPU raw kernel trace -# Cannot use wildcards in .rc files. Update this if there is a phone with -# more CPUs. - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu0/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu0/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu1/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu1/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu2/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu2/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu3/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu3/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu4/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu4/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu5/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu5/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu6/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu6/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu7/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu7/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu8/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu8/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu9/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu9/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu10/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu10/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu11/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu11/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu12/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu12/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu13/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu13/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu14/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu14/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu15/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu15/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu16/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu16/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu17/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu17/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu18/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu18/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu19/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu19/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu20/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu20/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu21/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu21/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu22/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu22/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace - -on property:ro.persistent_properties.ready=true - trigger mm_events_property_available - # Handle hyp tracing instance on late-init && property:ro.boot.hypervisor.vm.supported=1 diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index a5d176d8c4..fdb032b285 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -117,6 +117,7 @@ cc_defaults { "libdumpsys", "libserviceutils", "android.tracing.flags_c_lib", + "perfetto_flags_c_lib", ], } diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 888fb67b31..9e3e2b0468 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -57,6 +57,7 @@ #include <log/log_read.h> #include <math.h> #include <openssl/sha.h> +#include <perfetto_flags.h> #include <poll.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> @@ -190,7 +191,7 @@ void add_mountinfo(); #define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log" #define LINKERCONFIG_DIR "/linkerconfig" #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list" -#define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace" +#define SYSTEM_TRACE_DIR "/data/misc/perfetto-traces/bugreport" #define CGROUPFS_DIR "/sys/fs/cgroup" #define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk" #define DROPBOX_DIR "/data/system/dropbox" @@ -359,6 +360,31 @@ static bool CopyFileToFile(const std::string& input_file, const std::string& out return CopyFileToFd(input_file, out_fd.get()); } +template <typename Func> +size_t ForEachTrace(Func func) { + std::unique_ptr<DIR, decltype(&closedir)> traces_dir(opendir(SYSTEM_TRACE_DIR), closedir); + + if (traces_dir == nullptr) { + MYLOGW("Unable to open directory %s: %s\n", SYSTEM_TRACE_DIR, strerror(errno)); + return 0; + } + + size_t traces_found = 0; + struct dirent* entry = nullptr; + while ((entry = readdir(traces_dir.get()))) { + if (entry->d_type != DT_REG) { + continue; + } + std::string trace_path = std::string(SYSTEM_TRACE_DIR) + "/" + entry->d_name; + if (access(trace_path.c_str(), F_OK) != 0) { + continue; + } + ++traces_found; + func(trace_path); + } + return traces_found; +} + } // namespace } // namespace os } // namespace android @@ -1101,20 +1127,16 @@ static void MaybeAddSystemTraceToZip() { // This function copies into the .zip the system trace that was snapshotted // 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 MaybeSnapshotSystemTraceAsync() was invoked - if (!PropertiesHelper::IsUserBuild()) { - MYLOGI( - "No system traces found. Check for previously uploaded traces by looking for " - "go/trace-uuid in logcat") - } - return; + size_t traces_found = android::os::ForEachTrace([&](const std::string& trace_path) { + ds.AddZipEntry(ZIP_ROOT_DIR + trace_path, trace_path); + android::os::UnlinkAndLogOnError(trace_path); + }); + + if (traces_found == 0 && !PropertiesHelper::IsUserBuild()) { + MYLOGI( + "No system traces found. Check for previously uploaded traces by looking for " + "go/trace-uuid in logcat") } - ds.AddZipEntry( - ZIP_ROOT_DIR + SYSTEM_TRACE_SNAPSHOT, - SYSTEM_TRACE_SNAPSHOT); - android::os::UnlinkAndLogOnError(SYSTEM_TRACE_SNAPSHOT); } static void DumpVisibleWindowViews() { @@ -3412,8 +3434,8 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // duration is logged into MYLOG instead. PrintHeader(); - bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0; - if (options_->use_predumped_ui_data && !system_trace_exists) { + size_t trace_count = android::os::ForEachTrace([](const std::string&) {}); + if (options_->use_predumped_ui_data && trace_count == 0) { MYLOGW("Ignoring 'use predumped data' flag because no predumped data is available"); options_->use_predumped_ui_data = false; } @@ -3560,20 +3582,24 @@ std::future<std::string> Dumpstate::MaybeSnapshotSystemTraceAsync() { } // If a stale file exists already, remove it. - unlink(SYSTEM_TRACE_SNAPSHOT); + android::os::ForEachTrace([&](const std::string& trace_path) { unlink(trace_path.c_str()); }); 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. + // If one or more background system traces are happening and are marked as + // "suitable for bugreport" (bugreport_score > 0 in the trace config), this command + // will snapshot them into SYSTEM_TRACE_DIR. + // 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. + const char* cmd_arg = perfetto::flags::save_all_traces_in_bugreport() + ? "--save-all-for-bugreport" + : "--save-for-bugreport"; RunCommand( - SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"}, + SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", cmd_arg}, CommandOptions::WithTimeout(30).DropRoot().CloseAllFileDescriptorsOnExec().Build(), false, outFd); // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip diff --git a/cmds/dumpstate/dumpstate_smoke_test.xml b/cmds/dumpstate/dumpstate_smoke_test.xml index 0aff200dd2..7e3307d292 100644 --- a/cmds/dumpstate/dumpstate_smoke_test.xml +++ b/cmds/dumpstate/dumpstate_smoke_test.xml @@ -22,7 +22,9 @@ <option name="cleanup" value="true" /> <option name="push" value="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" /> </target_preparer> - + <target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer"> + <option name="flag-value" value="perfetto/perfetto.flags.save_all_traces_in_bugreport=true" /> + </target_preparer> <test class="com.android.tradefed.testtype.GTest" > <option name="native-test-device-path" value="/data/local/tmp" /> <option name="module-name" value="dumpstate_smoke_test" /> diff --git a/cmds/dumpstate/res/default_screenshot.png b/cmds/dumpstate/res/default_screenshot.png Binary files differindex 10f36aa52b..1e14306504 100644 --- a/cmds/dumpstate/res/default_screenshot.png +++ b/cmds/dumpstate/res/default_screenshot.png diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index a29923a4c1..c72847c053 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -24,8 +24,10 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <libgen.h> +#include <signal.h> #include <ziparchive/zip_archive.h> +#include <cstdio> #include <fstream> #include <regex> @@ -603,6 +605,93 @@ TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) { listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); } +class DumpstateTracingTest : public Test { + protected: + void TearDown() override { + for (int pid : bg_process_pids) { + kill(pid, SIGKILL); + } + } + + void StartTracing(const std::string& config) { + // Write the perfetto config into a file. + const int id = static_cast<int>(bg_process_pids.size()); + char cfg[64]; + snprintf(cfg, sizeof(cfg), "/data/misc/perfetto-configs/br-%d", id); + unlink(cfg); // Remove the config file if it exists already. + FILE* f = fopen(cfg, "w"); + ASSERT_NE(f, nullptr); + fputs(config.c_str(), f); + fclose(f); + + // Invoke perfetto to start tracing. + char cmd[255]; + snprintf(cmd, sizeof(cmd), "perfetto --background-wait --txt -o /dev/null -c %s", cfg); + FILE* proc = popen(cmd, "r"); + ASSERT_NE(proc, nullptr); + + // Read back the PID of the background process. We will use it to kill + // all tracing sessions when the test ends or fails. + char pid_str[32]{}; + ASSERT_NE(fgets(pid_str, sizeof(pid_str), proc), nullptr); + int pid = atoi(pid_str); + bg_process_pids.push_back(pid); + + pclose(proc); + unlink(cfg); + } + + std::vector<int> bg_process_pids; +}; + +TEST_F(DumpstateTracingTest, ManyTracesInBugreport) { + // Note the trace duration is irrelevant and is only an upper bound. + // Tracing is stopped as soon as the bugreport.zip creation ends. + StartTracing(R"( +buffers { size_kb: 4096 } +data_sources { + config { + name: "linux.ftrace" + } +} + +duration_ms: 120000 +bugreport_filename: "sys.pftrace" +bugreport_score: 100 +)"); + + StartTracing(R"( +buffers { size_kb: 4096 } +data_sources { + config { + name: "linux.ftrace" + } +} + +duration_ms: 120000 +bugreport_score: 50 +bugreport_filename: "mem.pftrace" +)"); + + ZippedBugreportGenerationTest::GenerateBugreport(); + std::string zip_path = ZippedBugreportGenerationTest::getZipFilePath(); + ZipArchiveHandle handle; + ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0); + + const char* kExpectedEntries[]{ + "FS/data/misc/perfetto-traces/bugreport/sys.pftrace", + "FS/data/misc/perfetto-traces/bugreport/mem.pftrace", + }; + + // Check that the bugreport contains both traces. + for (const char* file_path : kExpectedEntries) { + ZipEntry entry{}; + GetEntry(handle, file_path, &entry); + EXPECT_GT(entry.uncompressed_length, 100); + } + CloseArchive(handle); +} + } // namespace dumpstate } // namespace os } // namespace android diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp index 77e732805e..a2e171fa3b 100644 --- a/cmds/flatland/GLHelper.cpp +++ b/cmds/flatland/GLHelper.cpp @@ -202,26 +202,13 @@ bool GLHelper::getShaderProgram(const char* name, GLuint* outPgm) { } bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h, - sp<GLConsumer>* glConsumer, EGLSurface* surface) { -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - sp<GLConsumer> glc = new GLConsumer(name, GL_TEXTURE_EXTERNAL_OES, false, true); + sp<GLConsumer>* glConsumer, EGLSurface* surface) { + auto [glc, surf] = GLConsumer::create(name, GL_TEXTURE_EXTERNAL_OES, false, true); glc->setDefaultBufferSize(w, h); - glc->getSurface()->setMaxDequeuedBufferCount(2); glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); + surf->setMaxDequeuedBufferCount(2); + sp<ANativeWindow> anw = surf; - sp<ANativeWindow> anw = glc->getSurface(); -#else - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<GLConsumer> glc = new GLConsumer(consumer, name, - GL_TEXTURE_EXTERNAL_OES, false, true); - glc->setDefaultBufferSize(w, h); - producer->setMaxDequeuedBufferCount(2); - glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); - - sp<ANativeWindow> anw = new Surface(producer); -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr); if (s == EGL_NO_SURFACE) { fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); @@ -254,7 +241,7 @@ bool GLHelper::createWindowSurface(uint32_t w, uint32_t h, status_t err; if (mSurfaceComposerClient == nullptr) { - mSurfaceComposerClient = new SurfaceComposerClient; + mSurfaceComposerClient = sp<SurfaceComposerClient>::make(); } err = mSurfaceComposerClient->initCheck(); if (err != NO_ERROR) { diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp index 36dcbca0a3..b87ef2dc36 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -25,13 +25,8 @@ cc_defaults { name: "idlcli-defaults", shared_libs: [ "android.hardware.vibrator-V3-ndk", - "android.hardware.vibrator@1.0", - "android.hardware.vibrator@1.1", - "android.hardware.vibrator@1.2", - "android.hardware.vibrator@1.3", "libbase", "libbinder_ndk", - "libhidlbase", "liblog", "libutils", ], diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h index 262f2e50b6..dc52c57987 100644 --- a/cmds/idlcli/utils.h +++ b/cmds/idlcli/utils.h @@ -18,7 +18,6 @@ #define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ #include <android/binder_enums.h> -#include <hidl/HidlSupport.h> #include <iomanip> #include <iostream> diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h index b9434950f6..1a9993e5e7 100644 --- a/cmds/idlcli/vibrator.h +++ b/cmds/idlcli/vibrator.h @@ -22,102 +22,30 @@ #include <aidl/android/hardware/vibrator/IVibratorManager.h> #include <android/binder_manager.h> #include <android/binder_process.h> -#include <android/hardware/vibrator/1.3/IVibrator.h> #include "IdlCli.h" #include "utils.h" namespace android { -using hardware::Return; +using ::aidl::android::hardware::vibrator::IVibrator; using idlcli::IdlCli; -static constexpr int NUM_TRIES = 2; - -// Creates a Return<R> with STATUS::EX_NULL_POINTER. -template <class R> -inline R NullptrStatus() { - using ::android::hardware::Status; - return Status::fromExceptionCode(Status::EX_NULL_POINTER); -} - -template <> -inline ndk::ScopedAStatus NullptrStatus() { - return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NULL_POINTER)); -} - -template <typename I> inline auto getService(std::string name) { - const auto instance = std::string() + I::descriptor + "/" + name; + const auto instance = std::string() + IVibrator::descriptor + "/" + name; auto vibBinder = ndk::SpAIBinder(AServiceManager_checkService(instance.c_str())); - return I::fromBinder(vibBinder); -} - -template <> -inline auto getService<android::hardware::vibrator::V1_0::IVibrator>(std::string name) { - return android::hardware::vibrator::V1_0::IVibrator::getService(name); -} - -template <> -inline auto getService<android::hardware::vibrator::V1_1::IVibrator>(std::string name) { - return android::hardware::vibrator::V1_1::IVibrator::getService(name); -} - -template <> -inline auto getService<android::hardware::vibrator::V1_2::IVibrator>(std::string name) { - return android::hardware::vibrator::V1_2::IVibrator::getService(name); -} - -template <> -inline auto getService<android::hardware::vibrator::V1_3::IVibrator>(std::string name) { - return android::hardware::vibrator::V1_3::IVibrator::getService(name); + return IVibrator::fromBinder(vibBinder); } -template <typename I> -using shared_ptr = std::invoke_result_t<decltype(getService<I>)&, std::string>; - -template <typename I> -class HalWrapper { -public: - static std::unique_ptr<HalWrapper> Create() { - // Assume that if getService returns a nullptr, HAL is not available on the - // device. - const auto name = IdlCli::Get().getName(); - auto hal = getService<I>(name.empty() ? "default" : name); - return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr; - } - - template <class R, class... Args0, class... Args1> - R call(R (I::*fn)(Args0...), Args1&&... args1) { - return (*mHal.*fn)(std::forward<Args1>(args1)...); - } - -private: - HalWrapper(shared_ptr<I>&& hal) : mHal(std::move(hal)) {} - -private: - shared_ptr<I> mHal; -}; - -template <typename I> static auto getHal() { - static auto sHalWrapper = HalWrapper<I>::Create(); - return sHalWrapper.get(); -} - -template <class R, class I, class... Args0, class... Args1> -R halCall(R (I::*fn)(Args0...), Args1&&... args1) { - auto hal = getHal<I>(); - return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>(); + // Assume that if getService returns a nullptr, HAL is not available on the device. + const auto name = IdlCli::Get().getName(); + return getService(name.empty() ? "default" : name); } namespace idlcli { namespace vibrator { -namespace V1_0 = ::android::hardware::vibrator::V1_0; -namespace V1_1 = ::android::hardware::vibrator::V1_1; -namespace V1_2 = ::android::hardware::vibrator::V1_2; -namespace V1_3 = ::android::hardware::vibrator::V1_3; namespace aidl = ::aidl::android::hardware::vibrator; class VibratorCallback : public aidl::BnVibratorCallback { diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp index 9afa300c2b..cae690945c 100644 --- a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp +++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp @@ -51,21 +51,17 @@ class CommandAlwaysOnDisable : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId); - - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + auto status = hal->alwaysOnDisable(mId); + + std::cout << "Status: " << status.getDescription() << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } int32_t mId; diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp index bb7f9f284a..410ca52068 100644 --- a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp +++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp @@ -72,21 +72,17 @@ class CommandAlwaysOnEnable : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength); - - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + auto status = hal->alwaysOnEnable(mId, mEffect, mStrength); + + std::cout << "Status: " << status.getDescription() << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } int32_t mId; diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp index eb9008b68e..41acb98a95 100644 --- a/cmds/idlcli/vibrator/CommandCompose.cpp +++ b/cmds/idlcli/vibrator/CommandCompose.cpp @@ -89,7 +89,7 @@ class CommandCompose : public Command { } Status doMain(Args && /*args*/) override { - auto hal = getHal<aidl::IVibrator>(); + auto hal = getHal(); if (!hal) { return UNAVAILABLE; @@ -104,7 +104,7 @@ class CommandCompose : public Command { callback = ndk::SharedRefBase::make<VibratorCallback>(); } - auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback); + auto status = hal->compose(mComposite, callback); if (status.isOk() && callback) { callback->waitForComplete(); diff --git a/cmds/idlcli/vibrator/CommandComposePwle.cpp b/cmds/idlcli/vibrator/CommandComposePwle.cpp index b8308ce16f..5f6bf8677e 100644 --- a/cmds/idlcli/vibrator/CommandComposePwle.cpp +++ b/cmds/idlcli/vibrator/CommandComposePwle.cpp @@ -163,7 +163,7 @@ class CommandComposePwle : public Command { } Status doMain(Args && /*args*/) override { - auto hal = getHal<aidl::IVibrator>(); + auto hal = getHal(); if (!hal) { return UNAVAILABLE; @@ -178,7 +178,7 @@ class CommandComposePwle : public Command { callback = ndk::SharedRefBase::make<VibratorCallback>(); } - auto status = hal->call(&aidl::IVibrator::composePwle, mCompositePwle, callback); + auto status = hal->composePwle(mCompositePwle, callback); if (status.isOk() && callback) { callback->waitForComplete(); diff --git a/cmds/idlcli/vibrator/CommandComposePwleV2.cpp b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp index 6d3cf84a2e..bd682eacae 100644 --- a/cmds/idlcli/vibrator/CommandComposePwleV2.cpp +++ b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp @@ -108,7 +108,7 @@ class CommandComposePwleV2 : public Command { } Status doMain(Args&& /*args*/) override { - auto hal = getHal<aidl::IVibrator>(); + auto hal = getHal(); if (!hal) { return UNAVAILABLE; @@ -123,7 +123,7 @@ class CommandComposePwleV2 : public Command { callback = ndk::SharedRefBase::make<VibratorCallback>(); } - auto status = hal->call(&aidl::IVibrator::composePwleV2, mCompositePwle, callback); + auto status = hal->composePwleV2(mCompositePwle, callback); if (status.isOk() && callback) { callback->waitForComplete(); diff --git a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp index aa01a11237..44115e9a97 100644 --- a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp +++ b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp @@ -44,29 +44,38 @@ class CommandGetBandwidthAmplitudeMap : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; + auto hal = getHal(); + + if (!hal) { + return UNAVAILABLE; + } + std::vector<float> bandwidthAmplitude; float frequencyMinimumHz; float frequencyResolutionHz; - Status ret; - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = - hal->call(&aidl::IVibrator::getBandwidthAmplitudeMap, &bandwidthAmplitude); - statusStr = status.getDescription(); - ret = (status.isOk() ? OK : ERROR); + auto status = hal->getBandwidthAmplitudeMap(&bandwidthAmplitude); - status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz); - ret = (status.isOk() ? OK : ERROR); + if (!status.isOk()) { + std::cout << "Status: " << status.getDescription() << std::endl; + return ERROR; + } - status = - hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz); - ret = (status.isOk() ? OK : ERROR); - } else { - return UNAVAILABLE; + status = hal->getFrequencyMinimum(&frequencyMinimumHz); + + if (!status.isOk()) { + std::cout << "Status: " << status.getDescription() << std::endl; + return ERROR; } - std::cout << "Status: " << statusStr << std::endl; + status = hal->getFrequencyResolution(&frequencyResolutionHz); + + if (!status.isOk()) { + std::cout << "Status: " << status.getDescription() << std::endl; + return ERROR; + } + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Bandwidth Amplitude Map: " << std::endl; float frequency = frequencyMinimumHz; for (auto &e : bandwidthAmplitude) { @@ -74,7 +83,7 @@ class CommandGetBandwidthAmplitudeMap : public Command { frequency += frequencyResolutionHz; } - return ret; + return OK; } }; diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp index 303a9895e4..507d871cac 100644 --- a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp +++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp @@ -42,22 +42,19 @@ class CommandGetCapabilities : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - int32_t cap; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t cap; + auto status = hal->getCapabilities(&cap); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Capabilities: " << std::bitset<32>(cap) << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp index 10508bd4dc..1c1eb3c6ff 100644 --- a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp +++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp @@ -44,22 +44,19 @@ class CommandGetCompositionDelayMax : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - int32_t maxDelayMs; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t maxDelayMs; + auto status = hal->getCompositionDelayMax(&maxDelayMs); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Max Delay: " << maxDelayMs << " ms" << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp index 900cb18809..cfd4c53ceb 100644 --- a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp +++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp @@ -44,22 +44,19 @@ class CommandGetCompositionSizeMax : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - int32_t maxSize; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t maxSize; + auto status = hal->getCompositionSizeMax(&maxSize); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Max Size: " << maxSize << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp index 504c6482ad..2a614466ab 100644 --- a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp +++ b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp @@ -44,22 +44,19 @@ class CommandGetFrequencyMinimum : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - float frequencyMinimumHz; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + float frequencyMinimumHz; + auto status = hal->getFrequencyMinimum(&frequencyMinimumHz); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Minimum Frequency: " << frequencyMinimumHz << " Hz" << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp index de358385a0..157d6bf732 100644 --- a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp +++ b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp @@ -44,23 +44,19 @@ class CommandGetFrequencyResolution : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - float frequencyResolutionHz; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = - hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + float frequencyResolutionHz; + auto status = hal->getFrequencyResolution(&frequencyResolutionHz); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Frequency Resolution: " << frequencyResolutionHz << " Hz" << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp index 2edd0caf2e..2eb45100db 100644 --- a/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp +++ b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp @@ -46,26 +46,22 @@ class CommandGetFrequencyToOutputAccelerationMap : public Command { } Status doMain(Args&& /*args*/) override { - std::string statusStr; - std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap; - Status ret; - - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getFrequencyToOutputAccelerationMap, - &frequencyToOutputAccelerationMap); - statusStr = status.getDescription(); - ret = (status.isOk() ? OK : ERROR); - } else { + auto hal = getHal(); + + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap; + auto status = hal->getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Frequency to Output Amplitude Map: " << std::endl; for (auto& entry : frequencyToOutputAccelerationMap) { std::cout << entry.frequencyHz << " " << entry.maxOutputAccelerationGs << std::endl; } - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp index 460d39e64f..c957f6b316 100644 --- a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp +++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp @@ -57,22 +57,19 @@ class CommandGetPrimitiveDuration : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - int32_t duration; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t duration; + auto status = hal->getPrimitiveDuration(mPrimitive, &duration); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Duration: " << duration << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } CompositePrimitive mPrimitive; diff --git a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp index b2c35519eb..c1b027833b 100644 --- a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp +++ b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp @@ -44,22 +44,19 @@ class CommandGetPwleCompositionSizeMax : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - int32_t maxSize; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getPwleCompositionSizeMax, &maxSize); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t maxSize; + auto status = hal->getPwleCompositionSizeMax(&maxSize); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Max Size: " << maxSize << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp index 90819731c4..ed00ba0c13 100644 --- a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp +++ b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp @@ -44,22 +44,19 @@ class CommandGetPwlePrimitiveDurationMax : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - int32_t maxDurationMs; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getPwlePrimitiveDurationMax, &maxDurationMs); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t maxDurationMs; + auto status = hal->getPwlePrimitiveDurationMax(&maxDurationMs); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp index cca072c46f..f780b8bfd7 100644 --- a/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp +++ b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp @@ -44,22 +44,19 @@ class CommandGetPwleV2CompositionSizeMax : public Command { } Status doMain(Args&& /*args*/) override { - std::string statusStr; - int32_t maxSize; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getPwleV2CompositionSizeMax, &maxSize); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t maxSize; + auto status = hal->getPwleV2CompositionSizeMax(&maxSize); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Max Size: " << maxSize << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp index dbbfe1a6d4..e84e969e7c 100644 --- a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp +++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp @@ -44,23 +44,19 @@ class CommandGetPwleV2PrimitiveDurationMaxMillis : public Command { } Status doMain(Args&& /*args*/) override { - std::string statusStr; - int32_t maxDurationMs; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMaxMillis, - &maxDurationMs); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t maxDurationMs; + auto status = hal->getPwleV2PrimitiveDurationMaxMillis(&maxDurationMs); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp index 09225c49c4..448fd2adc2 100644 --- a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp +++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp @@ -44,23 +44,19 @@ class CommandGetPwleV2PrimitiveDurationMinMillis : public Command { } Status doMain(Args&& /*args*/) override { - std::string statusStr; - int32_t minDurationMs; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMinMillis, - &minDurationMs); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + int32_t minDurationMs; + auto status = hal->getPwleV2PrimitiveDurationMinMillis(&minDurationMs); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Primitive duration min: " << minDurationMs << " ms" << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetQFactor.cpp b/cmds/idlcli/vibrator/CommandGetQFactor.cpp index a2681e905b..e04bad9384 100644 --- a/cmds/idlcli/vibrator/CommandGetQFactor.cpp +++ b/cmds/idlcli/vibrator/CommandGetQFactor.cpp @@ -42,22 +42,19 @@ class CommandGetQFactor : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - float qFactor; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getQFactor, &qFactor); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + float qFactor; + auto status = hal->getQFactor(&qFactor); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Q Factor: " << qFactor << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp index 81a6391de5..e222ea656e 100644 --- a/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp +++ b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp @@ -44,22 +44,19 @@ class CommandGetResonantFrequency : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - float resonantFrequencyHz; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getResonantFrequency, &resonantFrequencyHz); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + float resonantFrequencyHz; + auto status = hal->getResonantFrequency(&resonantFrequencyHz); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Resonant Frequency: " << resonantFrequencyHz << " Hz" << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp index edfcd9195a..9b05540fce 100644 --- a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp +++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp @@ -44,25 +44,22 @@ class CommandGetSupportedAlwaysOnEffects : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - std::vector<Effect> effects; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + std::vector<Effect> effects; + auto status = hal->getSupportedAlwaysOnEffects(&effects); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Effects:" << std::endl; for (auto &e : effects) { std::cout << " " << toString(e) << std::endl; } - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp index b326e07c22..f95f682fc0 100644 --- a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp +++ b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp @@ -44,25 +44,22 @@ class CommandGetSupportedBraking : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - std::vector<Braking> braking; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getSupportedBraking, &braking); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + std::vector<Braking> braking; + auto status = hal->getSupportedBraking(&braking); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Braking Mechanisms:" << std::endl; for (auto &e : braking) { std::cout << " " << toString(e) << std::endl; } - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp index 7658f22def..05de1b87cf 100644 --- a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp +++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp @@ -44,25 +44,22 @@ class CommandGetSupportedEffects : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - std::vector<Effect> effects; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + std::vector<Effect> effects; + auto status = hal->getSupportedEffects(&effects); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Effects:" << std::endl; for (auto &e : effects) { std::cout << " " << toString(e) << std::endl; } - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp index d101681914..0f33f0f2e9 100644 --- a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp +++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp @@ -44,25 +44,22 @@ class CommandGetSupportedPrimitives : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - std::vector<CompositePrimitive> primitives; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + std::vector<CompositePrimitive> primitives; + auto status = hal->getSupportedPrimitives(&primitives); + + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Primitives:" << std::endl; for (auto &e : primitives) { std::cout << " " << toString(e) << std::endl; } - return ret; + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp index cedb9fec06..e55b44a9c8 100644 --- a/cmds/idlcli/vibrator/CommandOff.cpp +++ b/cmds/idlcli/vibrator/CommandOff.cpp @@ -42,24 +42,17 @@ class CommandOff : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - Status ret; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::off); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else if (auto hal = getHal<V1_0::IVibrator>()) { - auto status = hal->call(&V1_0::IVibrator::off); - statusStr = toString(status); - ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR; - } else { + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + auto status = hal->off(); - return ret; + std::cout << "Status: " << status.getDescription() << std::endl; + + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp index 8212fc14a7..856c219750 100644 --- a/cmds/idlcli/vibrator/CommandOn.cpp +++ b/cmds/idlcli/vibrator/CommandOn.cpp @@ -67,34 +67,27 @@ class CommandOn : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - Status ret; - std::shared_ptr<VibratorCallback> callback; + auto hal = getHal(); - if (auto hal = getHal<aidl::IVibrator>()) { - ABinderProcess_setThreadPoolMaxThreadCount(1); - ABinderProcess_startThreadPool(); + if (!hal) { + return UNAVAILABLE; + } - int32_t cap; - hal->call(&aidl::IVibrator::getCapabilities, &cap); + std::shared_ptr<VibratorCallback> callback; - if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) { - callback = ndk::SharedRefBase::make<VibratorCallback>(); - } + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); - auto status = hal->call(&aidl::IVibrator::on, mDuration, callback); + int32_t cap; + hal->getCapabilities(&cap); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else if (auto hal = getHal<V1_0::IVibrator>()) { - auto status = hal->call(&V1_0::IVibrator::on, mDuration); - statusStr = toString(status); - ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR; - } else { - return UNAVAILABLE; + if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); } - if (ret == OK && mBlocking) { + auto status = hal->on(mDuration, callback); + + if (status.isOk() && mBlocking) { if (callback) { callback->waitForComplete(); } else { @@ -102,9 +95,9 @@ class CommandOn : public Command { } } - std::cout << "Status: " << statusStr << std::endl; + std::cout << "Status: " << status.getDescription() << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } bool mBlocking; diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp index c897686cbe..0a354e2c5a 100644 --- a/cmds/idlcli/vibrator/CommandPerform.cpp +++ b/cmds/idlcli/vibrator/CommandPerform.cpp @@ -28,34 +28,6 @@ class CommandVibrator; namespace vibrator { -/* - * The following static asserts are only relevant here because the argument - * parser uses a single implementation for determining the string names. - */ -static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) == - static_cast<uint8_t>(aidl::EffectStrength::LIGHT)); -static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) == - static_cast<uint8_t>(aidl::EffectStrength::MEDIUM)); -static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) == - static_cast<uint8_t>(aidl::EffectStrength::STRONG)); -static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) == - static_cast<uint8_t>(aidl::Effect::CLICK)); -static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) == - static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK)); -static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK)); -static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD)); -static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP)); -static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) == - static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK)); -static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_1)); -static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_2)); -static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_15)); -static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == - static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); - using aidl::Effect; using aidl::EffectStrength; @@ -107,61 +79,31 @@ class CommandPerform : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; + auto hal = getHal(); + + if (!hal) { + return UNAVAILABLE; + } + uint32_t lengthMs; - Status ret; std::shared_ptr<VibratorCallback> callback; - if (auto hal = getHal<aidl::IVibrator>()) { - ABinderProcess_setThreadPoolMaxThreadCount(1); - ABinderProcess_startThreadPool(); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); - int32_t cap; - hal->call(&aidl::IVibrator::getCapabilities, &cap); + int32_t cap; + hal->getCapabilities(&cap); - if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) { - callback = ndk::SharedRefBase::make<VibratorCallback>(); - } - - int32_t aidlLengthMs; - auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback, - &aidlLengthMs); + if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); + } - statusStr = status.getDescription(); - lengthMs = static_cast<uint32_t>(aidlLengthMs); - ret = status.isOk() ? OK : ERROR; - } else { - Return<void> hidlRet; - V1_0::Status status; - auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) { - status = retStatus; - lengthMs = retLengthMs; - }; - - if (auto hal = getHal<V1_3::IVibrator>()) { - hidlRet = - hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect), - static_cast<V1_0::EffectStrength>(mStrength), callback); - } else if (auto hal = getHal<V1_2::IVibrator>()) { - hidlRet = - hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect), - static_cast<V1_0::EffectStrength>(mStrength), callback); - } else if (auto hal = getHal<V1_1::IVibrator>()) { - hidlRet = hal->call(&V1_1::IVibrator::perform_1_1, - static_cast<V1_1::Effect_1_1>(mEffect), - static_cast<V1_0::EffectStrength>(mStrength), callback); - } else if (auto hal = getHal<V1_0::IVibrator>()) { - hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect), - static_cast<V1_0::EffectStrength>(mStrength), callback); - } else { - return UNAVAILABLE; - } + int32_t aidlLengthMs; + auto status = hal->perform(mEffect, mStrength, callback, &aidlLengthMs); - statusStr = toString(status); - ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR; - } + lengthMs = static_cast<uint32_t>(aidlLengthMs); - if (ret == OK && mBlocking) { + if (status.isOk() && mBlocking) { if (callback) { callback->waitForComplete(); } else { @@ -169,10 +111,10 @@ class CommandPerform : public Command { } } - std::cout << "Status: " << statusStr << std::endl; + std::cout << "Status: " << status.getDescription() << std::endl; std::cout << "Length: " << lengthMs << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } bool mBlocking; diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp index 8b8058c4fd..8050723729 100644 --- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp +++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp @@ -50,25 +50,17 @@ class CommandSetAmplitude : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - Status ret; - - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::setAmplitude, - static_cast<float>(mAmplitude) / UINT8_MAX); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else if (auto hal = getHal<V1_0::IVibrator>()) { - auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude); - statusStr = toString(status); - ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR; - } else { + auto hal = getHal(); + + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + auto status = hal->setAmplitude(static_cast<float>(mAmplitude) / UINT8_MAX); + + std::cout << "Status: " << status.getDescription() << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } uint8_t mAmplitude; diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp index 179579310a..8f8d4b7641 100644 --- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp +++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp @@ -48,24 +48,17 @@ class CommandSetExternalControl : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - Status ret; - - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else if (auto hal = getHal<V1_3::IVibrator>()) { - auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable); - statusStr = toString(status); - ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR; - } else { + auto hal = getHal(); + + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + auto status = hal->setExternalControl(mEnable); + + std::cout << "Status: " << status.getDescription() << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } bool mEnable; diff --git a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp index cdc529a2f3..31ee954f63 100644 --- a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp +++ b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp @@ -42,15 +42,22 @@ class CommandSupportsAmplitudeControl : public Command { } Status doMain(Args && /*args*/) override { - auto ret = halCall(&V1_0::IVibrator::supportsAmplitudeControl); + auto hal = getHal(); - if (!ret.isOk()) { + if (!hal) { return UNAVAILABLE; } - std::cout << "Result: " << std::boolalpha << ret << std::endl; + int32_t cap; - return OK; + auto status = hal->getCapabilities(&cap); + + bool hasAmplitudeControl = cap & IVibrator::CAP_AMPLITUDE_CONTROL; + + std::cout << "Status: " << status.getDescription() << std::endl; + std::cout << "Result: " << std::boolalpha << hasAmplitudeControl << std::endl; + + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp index ed15d76286..f0c542cfe3 100644 --- a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp +++ b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp @@ -42,15 +42,22 @@ class CommandSupportsExternalControl : public Command { } Status doMain(Args && /*args*/) override { - auto ret = halCall(&V1_3::IVibrator::supportsExternalControl); + auto hal = getHal(); - if (!ret.isOk()) { + if (!hal) { return UNAVAILABLE; } - std::cout << "Result: " << std::boolalpha << ret << std::endl; + int32_t cap; - return OK; + auto status = hal->getCapabilities(&cap); + + bool hasExternalControl = cap & IVibrator::CAP_EXTERNAL_CONTROL; + + std::cout << "Status: " << status.getDescription() << std::endl; + std::cout << "Result: " << std::boolalpha << hasExternalControl << std::endl; + + return status.isOk() ? OK : ERROR; } }; diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING index fc4cfc98dc..d53c94ba55 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -18,10 +18,6 @@ { "name": "run_dex2oat_test" }, - // AdoptableHostTest moves packages, part of which is handled by installd - { - "name": "AdoptableHostTest" - }, { "name": "CtsUsesLibraryHostTestCases" }, diff --git a/data/etc/Android.bp b/data/etc/Android.bp index cd8ca89de5..f320504551 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -119,12 +119,24 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.nfc.ese.prebuilt.xml", + src: "android.hardware.nfc.ese.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.nfc.hce.prebuilt.xml", src: "android.hardware.nfc.hce.xml", defaults: ["frameworks_native_data_etc_defaults"], } prebuilt_etc { + name: "android.hardware.nfc.hcef.prebuilt.xml", + src: "android.hardware.nfc.hcef.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.reboot_escrow.prebuilt.xml", src: "android.hardware.reboot_escrow.xml", defaults: ["frameworks_native_data_etc_defaults"], @@ -317,6 +329,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.telephony.messaging.prebuilt.xml", + src: "android.hardware.telephony.messaging.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"], @@ -505,6 +523,12 @@ prebuilt_etc { } prebuilt_etc { + name: "com.nxp.mifare.prebuilt.xml", + src: "com.nxp.mifare.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "go_handheld_core_hardware.prebuilt.xml", src: "go_handheld_core_hardware.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/data/etc/android.hardware.telephony.messaging.xml b/data/etc/android.hardware.telephony.messaging.xml new file mode 100644 index 0000000000..0e96123625 --- /dev/null +++ b/data/etc/android.hardware.telephony.messaging.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2025 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 with messaging. --> +<permissions> + <feature name="android.hardware.telephony" /> + <feature name="android.hardware.telephony.radio.access" /> + <feature name="android.hardware.telephony.subscription" /> + <feature name="android.hardware.telephony.messaging" /> +</permissions> diff --git a/include/OWNERS b/include/OWNERS index c98e87aa74..7f847e8015 100644 --- a/include/OWNERS +++ b/include/OWNERS @@ -1,5 +1,4 @@ alecmouri@google.com -alexeykuzmin@google.com dangittik@google.com jreck@google.com lajos@google.com diff --git a/include/android/configuration.h b/include/android/configuration.h index 46c7dfeceb..a291db0ecd 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -26,6 +26,7 @@ #ifndef ANDROID_CONFIGURATION_H #define ANDROID_CONFIGURATION_H +#include <stdint.h> #include <sys/cdefs.h> #include <android/asset_manager.h> diff --git a/include/android/input.h b/include/android/input.h index 5f445509fa..2f6c5b57ff 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -862,7 +862,7 @@ enum { AMOTION_EVENT_BUTTON_FORWARD = 1 << 4, AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = 1 << 5, AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = 1 << 6, - // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs) + // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs,/frameworks/native/services/inputflinger/tests/fuzzers/FuzzedInputStream.h) }; /** diff --git a/include/android/system_health.h b/include/android/system_health.h index bdb1413555..4494e32815 100644 --- a/include/android/system_health.h +++ b/include/android/system_health.h @@ -316,8 +316,6 @@ int ASystemHealth_getMaxCpuHeadroomTidsSize(size_t* _Nonnull outSize); /** * Gets the range of the calculation window size for CPU headroom. * - * In API version 36, the range will be a superset of [50, 10000]. - * * Available since API level 36. * * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds. @@ -332,8 +330,6 @@ __INTRODUCED_IN(36); /** * Gets the range of the calculation window size for GPU headroom. * - * In API version 36, the range will be a superset of [50, 10000]. - * * Available since API level 36. * * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds. diff --git a/include/ftl/ignore.h b/include/ftl/ignore.h new file mode 100644 index 0000000000..1468fa2498 --- /dev/null +++ b/include/ftl/ignore.h @@ -0,0 +1,42 @@ +/* + * Copyright 2025 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::ftl { + +// An alternative to `std::ignore` that makes it easy to ignore multiple values. +// +// Examples: +// +// void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) { +// // When invoked, all the arguments are ignored. +// ftl::ignore(arg1, arg2, arg3); +// } +// +// void ftl_ignore_single(int arg) { +// // It can be used like std::ignore to ignore a single value +// ftl::ignore = arg; +// } +// +inline constexpr struct { + // NOLINTNEXTLINE(misc-unconventional-assign-operator, readability-named-parameter) + constexpr auto operator=(auto&&) const -> decltype(*this) { return *this; } + // NOLINTNEXTLINE(readability-named-parameter) + constexpr void operator()(auto&&...) const {} +} ignore; + +} // namespace android::ftl
\ No newline at end of file diff --git a/include/input/DisplayTopologyGraph.h b/include/input/DisplayTopologyGraph.h index 3ae865a33a..9fc080d6f8 100644 --- a/include/input/DisplayTopologyGraph.h +++ b/include/input/DisplayTopologyGraph.h @@ -46,6 +46,8 @@ struct DisplayTopologyAdjacentDisplay { DisplayTopologyPosition position; // The offset in DP of the adjacent display, relative to the source display. float offsetDp; + + std::string dump() const; }; /** @@ -55,6 +57,9 @@ struct DisplayTopologyGraph { ui::LogicalDisplayId primaryDisplayId = ui::LogicalDisplayId::INVALID; std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> graph; std::unordered_map<ui::LogicalDisplayId, int> displaysDensity; + + bool isValid() const; + std::string dump() const; }; } // namespace android diff --git a/include/input/Input.h b/include/input/Input.h index e84023e5e5..002b3a7d15 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -316,6 +316,19 @@ struct PointerProperties; bool isStylusEvent(uint32_t source, const std::vector<PointerProperties>& properties); +bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties, + int32_t action); + +bool isFromMouse(uint32_t source, ToolType tooltype); + +bool isFromTouchpad(uint32_t source, ToolType tooltype); + +bool isFromDrawingTablet(uint32_t source, ToolType tooltype); + +bool isHoverAction(int32_t action); + +bool isMouseOrTouchpad(uint32_t sources); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. diff --git a/include/input/InputFlags.h b/include/input/InputFlags.h new file mode 100644 index 0000000000..16e754e210 --- /dev/null +++ b/include/input/InputFlags.h @@ -0,0 +1,49 @@ +/* + * Copyright 2025 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 { + +class InputFlags { +public: + /** + * Check if connected displays feature is enabled, either via the feature flag or settings + * override. Developer setting override allows enabling all the "desktop experiences" features + * including input related connected_displays_cursor flag. + * + * The developer settings override is prioritised over aconfig flags. Any tests that require + * applicable aconfig flags to be disabled with SCOPED_FLAG_OVERRIDE also need this developer + * option to be reset locally. + * + * Also note the developer setting override is only applicable to the desktop experiences + * related features. + * + * To enable only the input flag run: + * adb shell aflags enable com.android.input.flags.connected_displays_cursor + * To override this flag and enable all "desktop experiences" features run: + * adb shell aflags enable com.android.window.flags.enable_desktop_mode_through_dev_option + * adb shell setprop persist.wm.debug.desktop_experience_devopts 1 + */ + static bool connectedDisplaysCursorEnabled(); + + /** + * Check if both connectedDisplaysCursor and associatedDisplayCursorBugfix is enabled. + */ + static bool connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled(); +}; + +} // namespace android diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h index f4f4d5e05c..e22481fceb 100644 --- a/include/powermanager/PowerHalController.h +++ b/include/powermanager/PowerHalController.h @@ -73,6 +73,9 @@ public: int tgid, int uid) override; virtual HalResult<void> closeSessionChannel(int tgid, int uid) override; virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override; + virtual HalResult<void> sendCompositionData( + const std::vector<hal::CompositionData>& data) override; + virtual HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override; private: std::mutex mConnectedHalMutex; diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h index 42901821bc..17a4cd4eff 100644 --- a/include/powermanager/PowerHalWrapper.h +++ b/include/powermanager/PowerHalWrapper.h @@ -18,6 +18,8 @@ #include <aidl/android/hardware/power/Boost.h> #include <aidl/android/hardware/power/ChannelConfig.h> +#include <aidl/android/hardware/power/CompositionData.h> +#include <aidl/android/hardware/power/CompositionUpdate.h> #include <aidl/android/hardware/power/IPower.h> #include <aidl/android/hardware/power/IPowerHintSession.h> #include <aidl/android/hardware/power/Mode.h> @@ -37,6 +39,8 @@ namespace android { namespace power { +namespace hal = aidl::android::hardware::power; + // State of Power HAL support for individual apis. enum class HalSupport { UNKNOWN = 0, @@ -49,21 +53,20 @@ class HalWrapper { public: virtual ~HalWrapper() = default; - virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, - int32_t durationMs) = 0; - virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0; + virtual HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) = 0; + virtual HalResult<void> setMode(hal::Mode mode, bool enabled) = 0; virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) = 0; virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> 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; + hal::SessionTag tag, hal::SessionConfig* config) = 0; virtual HalResult<int64_t> getHintSessionPreferredRate() = 0; - virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, - int uid) = 0; + virtual HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) = 0; virtual HalResult<void> closeSessionChannel(int tgid, int uid) = 0; virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() = 0; + virtual HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) = 0; + virtual HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) = 0; }; // Empty Power HAL wrapper that ignores all api calls. @@ -72,21 +75,20 @@ public: EmptyHalWrapper() = default; ~EmptyHalWrapper() override = default; - 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<void> setBoost(hal::Boost boost, int32_t durationMs) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; HalResult<std::shared_ptr<PowerHintSessionWrapper>> 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; + hal::SessionTag tag, hal::SessionConfig* config) override; HalResult<int64_t> getHintSessionPreferredRate() override; - HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, - int uid) override; + HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) override; HalResult<void> closeSessionChannel(int tgid, int uid) override; HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override; + HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) override; + HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override; protected: virtual const char* getUnsupportedMessage(); @@ -99,9 +101,8 @@ public: : mHandleV1_0(std::move(handleV1_0)) {} ~HidlHalWrapperV1_0() override = default; - 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<void> setBoost(hal::Boost boost, int32_t durationMs) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; protected: const sp<hardware::power::V1_0::IPower> mHandleV1_0; @@ -127,9 +128,8 @@ protected: // Wrapper for the HIDL Power HAL v1.2. class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 { 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<void> setBoost(hal::Boost boost, int32_t durationMs) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; explicit HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2) : HidlHalWrapperV1_1(std::move(handleV1_2)) {} ~HidlHalWrapperV1_2() override = default; @@ -141,7 +141,7 @@ protected: // Wrapper for the HIDL Power HAL v1.3. class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 { public: - HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; explicit HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3) : HidlHalWrapperV1_2(std::move(handleV1_3)) {} ~HidlHalWrapperV1_3() override = default; @@ -153,26 +153,24 @@ protected: // Wrapper for the AIDL Power HAL. class AidlHalWrapper : public EmptyHalWrapper { public: - explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle) - : mHandle(std::move(handle)) {} + explicit AidlHalWrapper(std::shared_ptr<hal::IPower> handle) : mHandle(std::move(handle)) {} ~AidlHalWrapper() override = default; - 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<void> setBoost(hal::Boost boost, int32_t durationMs) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; HalResult<std::shared_ptr<PowerHintSessionWrapper>> 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; + hal::SessionTag tag, hal::SessionConfig* config) override; HalResult<int64_t> getHintSessionPreferredRate() override; - HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, - int uid) override; + HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) override; HalResult<void> closeSessionChannel(int tgid, int uid) override; HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override; + HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) override; + HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override; protected: const char* getUnsupportedMessage() override; @@ -181,16 +179,10 @@ private: // Control access to the boost and mode supported arrays. std::mutex mBoostMutex; std::mutex mModeMutex; - std::shared_ptr<aidl::android::hardware::power::IPower> mHandle; - std::array<HalSupport, - static_cast<int32_t>( - *(ndk::enum_range<aidl::android::hardware::power::Boost>().end() - 1)) + - 1> + std::shared_ptr<hal::IPower> mHandle; + std::array<HalSupport, static_cast<int32_t>(*(ndk::enum_range<hal::Boost>().end() - 1)) + 1> mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN}; - std::array<HalSupport, - static_cast<int32_t>( - *(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) + - 1> + std::array<HalSupport, static_cast<int32_t>(*(ndk::enum_range<hal::Mode>().end() - 1)) + 1> mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN}; }; diff --git a/include/powermanager/PowerHintSessionWrapper.h b/include/powermanager/PowerHintSessionWrapper.h index ba6fe77c80..0134e02f12 100644 --- a/include/powermanager/PowerHintSessionWrapper.h +++ b/include/powermanager/PowerHintSessionWrapper.h @@ -45,9 +45,11 @@ public: virtual HalResult<void> setMode(::aidl::android::hardware::power::SessionMode in_type, bool in_enabled); virtual HalResult<aidl::android::hardware::power::SessionConfig> getSessionConfig(); + std::optional<int> getSessionId(); private: std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mSession; + std::optional<int> mSessionId; int32_t mInterfaceVersion; }; diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS index 4dbb0eae5c..76811f2551 100644 --- a/libs/attestation/OWNERS +++ b/libs/attestation/OWNERS @@ -1,2 +1 @@ -chaviw@google.com -svv@google.com
\ No newline at end of file +svv@google.com diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index fb00d4f468..a3499c1963 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -277,15 +277,6 @@ cc_defaults { "-fvisibility=hidden", "-DBUILDING_LIBBINDER", ], - - target: { - vendor: { - // Trimming the exported symbols reveals a bug in vendor code, so - // disable it for the vendor variant for now. http://b/349657329 - // TODO: Fix the issue and remove this override. - cflags: ["-fvisibility=default"], - }, - }, } cc_defaults { @@ -827,6 +818,7 @@ cc_library { // so we restrict its visibility to the Trusty-specific packages. visibility: [ ":__subpackages__", + "//hardware/interfaces/security/see:__subpackages__", "//system/core/trusty:__subpackages__", "//vendor:__subpackages__", ], diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp index 7c0319aead..b1c8994b05 100644 --- a/libs/binder/BackendUnifiedServiceManager.cpp +++ b/libs/binder/BackendUnifiedServiceManager.cpp @@ -130,7 +130,13 @@ os::ServiceWithMetadata createServiceWithMetadata(const sp<IBinder>& service, bo bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) { sp<ProcessState> self = ProcessState::selfOrNull(); - if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) { + // Should not cache if process state could not be found, or if thread pool + // max could is not greater than zero. + if (!self) { + ALOGW("Service retrieved before binder threads started. If they are to be started, " + "consider starting binder threads earlier."); + return false; + } else if (self->getThreadPoolMaxTotalThreadCount() <= 0) { ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be " "implemented. serviceName: %s", serviceName.c_str()); diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index bc7ae37ff0..9883eb2672 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -445,6 +445,9 @@ status_t BBinder::linkToDeath( const sp<DeathRecipient>& /*recipient*/, void* /*cookie*/, uint32_t /*flags*/) { + // BBinder::linkToDeath is invalid because this process owns this binder. + // The DeathRecipient is called on BpBinders when the process owning the + // binder node dies. return INVALID_OPERATION; } diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index 16023ffa82..1f3a45a6df 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -188,7 +188,9 @@ status_t RpcSession::setupInetClient(const char* addr, unsigned int port) { } status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) { - return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t { + return setupClient([&, fd = std::move(fd), + request = std::move(request)](const std::vector<uint8_t>& sessionId, + bool incoming) mutable -> status_t { if (!fd.ok()) { fd = request(); if (!fd.ok()) return BAD_VALUE; @@ -476,8 +478,10 @@ sp<RpcServer> RpcSession::server() { return server; } -status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId, - bool incoming)>& connectAndInit) { +template <typename Fn, + typename /* = std::enable_if_t<std::is_invocable_r_v< + status_t, Fn, const std::vector<uint8_t>&, bool>> */> +status_t RpcSession::setupClient(Fn&& connectAndInit) { { RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once"); diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp index 3819fb6472..14c0bde7c5 100644 --- a/libs/binder/RpcTransportTipcAndroid.cpp +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -21,6 +21,7 @@ #include <log/log.h> #include <poll.h> #include <trusty/tipc.h> +#include <type_traits> #include "FdTrigger.h" #include "RpcState.h" @@ -32,6 +33,9 @@ using android::binder::unique_fd; namespace android { +// Corresponds to IPC_MAX_MSG_HANDLES in the Trusty kernel +constexpr size_t kMaxTipcHandles = 8; + // RpcTransport for writing Trusty IPC clients in Android. class RpcTransportTipcAndroid : public RpcTransport { public: @@ -78,12 +82,28 @@ public: FdTrigger* fdTrigger, iovec* iovs, int niovs, const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override { + bool sentFds = false; auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t { - // TODO: send ancillaryFds. For now, we just abort if anyone tries - // to send any. - LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(), - "File descriptors are not supported on Trusty yet"); - return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0)); + trusty_shm shms[kMaxTipcHandles] = {{0}}; + ssize_t shm_count = 0; + + if (!sentFds && ancillaryFds != nullptr && !ancillaryFds->empty()) { + if (ancillaryFds->size() > kMaxTipcHandles) { + ALOGE("Too many file descriptors for TIPC: %zu", ancillaryFds->size()); + errno = EINVAL; + return -1; + } + for (const auto& fdVariant : *ancillaryFds) { + shms[shm_count++] = {std::visit([](const auto& fd) { return fd.get(); }, + fdVariant), + TRUSTY_SEND_SECURE_OR_SHARE}; + } + } + + auto ret = TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, + (shm_count == 0) ? nullptr : shms, shm_count)); + sentFds |= ret >= 0; + return ret; }; status_t status = interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn, diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS index 31005184bf..2617a16f69 100644 --- a/libs/binder/aidl/android/content/pm/OWNERS +++ b/libs/binder/aidl/android/content/pm/OWNERS @@ -1,5 +1,4 @@ +michaelwr@google.com narayan@google.com patb@google.com -svetoslavganov@google.com -toddke@google.com -patb@google.com
\ No newline at end of file +schfan@google.com diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index af37bf29a4..c9f92da009 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -25,6 +25,7 @@ #include <map> #include <optional> +#include <type_traits> #include <vector> namespace android { @@ -291,9 +292,14 @@ private: // join on thread passed to preJoinThreadOwnership static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result); - [[nodiscard]] status_t setupClient( - const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>& - connectAndInit); + // This is a workaround to support move-only functors. + // TODO: use std::move_only_function when it becomes available. + template <typename Fn, + // Fn must be a callable type taking (const std::vector<uint8_t>&, bool) and returning + // status_t + typename = std::enable_if_t< + std::is_invocable_r_v<status_t, Fn, const std::vector<uint8_t>&, bool>>> + [[nodiscard]] status_t setupClient(Fn&& connectAndInit); [[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address); [[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address, const std::vector<uint8_t>& sessionId, diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h index bcbd14f9d4..e848385418 100644 --- a/libs/binder/include/binder/SafeInterface.h +++ b/libs/binder/include/binder/SafeInterface.h @@ -79,7 +79,7 @@ public: template <typename T> typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read( const Parcel& parcel, sp<T>* t) const { - *t = new T{}; + *t = sp<T>::make(); return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); }); } template <typename T> diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 48c0ea636b..1949cbb039 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -134,9 +134,14 @@ AIBinder* ARpcSession_setupInet(ARpcSession* session, const char* address, unsig // // param will be passed to requestFd. Callers can use param to pass contexts to // the requestFd function. +// +// paramDeleteFd will be called when requestFd and param are no longer in use. +// Callers can pass a function deleting param if necessary. Callers can set +// paramDeleteFd to null if callers doesn't need to clean up param. AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session, int (*requestFd)(void* param), - void* param); + void* param, + void (*paramDeleteFd)(void* param)); // Sets the file descriptor transport mode for this session. void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session, diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index 64b1be25d2..8177fb07db 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -248,9 +248,18 @@ AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsign #endif // __TRUSTY__ AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param), - void* param) { + void* param, void (*paramDeleteFd)(void* param)) { auto session = handleToStrongPointer<RpcSession>(handle); - auto request = [=] { return unique_fd{requestFd(param)}; }; + auto deleter = [=](void* param) { + if (paramDeleteFd) { + paramDeleteFd(param); + } + }; + // TODO: use unique_ptr once setupPreconnectedClient uses std::move_only_function. + std::shared_ptr<void> sharedParam(param, deleter); + auto request = [=, sharedParam = std::move(sharedParam)] { + return unique_fd{requestFd(sharedParam.get())}; + }; if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str()); return nullptr; diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h index 89f21dda21..783e11f0e9 100644 --- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h +++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h @@ -62,6 +62,8 @@ __attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() _ * This must be called before the object is sent to another process. * Aborts on invalid values. Not thread safe. * + * This overrides the setting in ABinderProcess_disableBackgroundScheduling. + * * \param binder local server binder to set the policy for * \param policy scheduler policy as defined in linux UAPI * \param priority priority. [-20..19] for SCHED_NORMAL, [1..99] for RT diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h index 6aff994a15..2432099d6e 100644 --- a/libs/binder/ndk/include_platform/android/binder_process.h +++ b/libs/binder/ndk/include_platform/android/binder_process.h @@ -75,6 +75,19 @@ bool ABinderProcess_isThreadPoolStarted(void); void ABinderProcess_joinThreadPool(void); /** + * Disables (or enables) background scheduling. + * + * By default, binder threads execute at a lower priority. However, this can cause + * priority inversion, so it is recommended to be disabled in high priority + * or in system processes. + * + * See also AIBinder_setMinSchedulerPolicy, which overrides this setting. + * + * \param disable whether to disable background scheduling + */ +void ABinderProcess_disableBackgroundScheduling(bool disable); + +/** * This gives you an fd to wait on. Whenever data is available on the fd, * ABinderProcess_handlePolledCommands can be called to handle binder queries. * This is expected to be used in a single threaded process which waits on diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index 089c775eca..80502056ee 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -27,6 +27,10 @@ constexpr binder_flags_t FLAG_PRIVATE_VENDOR = 0x10000000; #if defined(__ANDROID_VENDOR__) +#if defined(__ANDROID_PRODUCT__) +#error "build bug: product is not part of the vendor half of the Treble system/vendor split" +#endif + /** * Private addition to binder_flag_t. */ diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index a63716522e..d4eb8c7579 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -229,6 +229,7 @@ LIBBINDER_NDK_PLATFORM { AIBinder_fromPlatformBinder*; AIBinder_toPlatformBinder*; AParcel_viewPlatformParcel*; + ABinderProcess_disableBackgroundScheduling; }; local: *; diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp index 0072ac3d3e..bcdb9594fa 100644 --- a/libs/binder/ndk/process.cpp +++ b/libs/binder/ndk/process.cpp @@ -36,6 +36,10 @@ void ABinderProcess_joinThreadPool(void) { IPCThreadState::self()->joinThreadPool(); } +void ABinderProcess_disableBackgroundScheduling(bool disable) { + IPCThreadState::disableBackgroundScheduling(disable); +} + binder_status_t ABinderProcess_setupPolling(int* fd) { return IPCThreadState::self()->setupPolling(fd); } diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index 7e5c9ddc35..774bb21efe 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -20,6 +20,8 @@ mod server; mod session; pub use server::RpcServer; +#[cfg(target_os = "trusty")] +pub use server::RpcServerConnection; #[cfg(not(target_os = "trusty"))] pub use server::RpcServerRef; pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef}; diff --git a/libs/binder/rust/rpcbinder/src/server/trusty.rs b/libs/binder/rust/rpcbinder/src/server/trusty.rs index fe45decf25..54d82d5bd0 100644 --- a/libs/binder/rust/rpcbinder/src/server/trusty.rs +++ b/libs/binder/rust/rpcbinder/src/server/trusty.rs @@ -106,6 +106,10 @@ pub struct RpcServerConnection { ctx: *mut c_void, } +// SAFETY: The opaque handle: `ctx` points into a dynamic allocation, +// and not tied to anything specific to the current thread. +unsafe impl Send for RpcServerConnection {} + impl Drop for RpcServerConnection { fn drop(&mut self) { // We do not need to close handle_fd since we do not own it. diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs index 09688a21a7..411b9de849 100644 --- a/libs/binder/rust/rpcbinder/src/session.rs +++ b/libs/binder/rust/rpcbinder/src/session.rs @@ -195,11 +195,13 @@ impl RpcSessionRef { /// take ownership of) file descriptors already connected to it. pub fn setup_preconnected_client<T: FromIBinder + ?Sized>( &self, - mut request_fd: impl FnMut() -> Option<RawFd>, + request_fd: impl FnMut() -> Option<RawFd>, ) -> Result<Strong<T>, StatusCode> { - // Double reference the factory because trait objects aren't FFI safe. - let mut request_fd_ref: RequestFd = &mut request_fd; - let param = &mut request_fd_ref as *mut RequestFd as *mut c_void; + // Trait objects aren't FFI safe, so *mut c_void can't be converted back to + // *mut dyn FnMut() -> Option<RawFd>>. Double box the factory to make it possible to get + // the factory from *mut c_void (to *mut Box<dyn<...>>) in the callbacks. + let request_fd_box: Box<dyn FnMut() -> Option<RawFd>> = Box::new(request_fd); + let param = Box::into_raw(Box::new(request_fd_box)) as *mut c_void; // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership @@ -209,6 +211,7 @@ impl RpcSessionRef { self.as_ptr(), Some(request_fd_wrapper), param, + Some(param_delete_fd_wrapper), )) }; Self::get_interface(service) @@ -225,13 +228,18 @@ impl RpcSessionRef { } } -type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>; - unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int { - let request_fd_ptr = param as *mut RequestFd; + let request_fd_ptr = param as *mut Box<dyn FnMut() -> Option<RawFd>>; // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the // BinderFdFactory reference, with param being a properly aligned non-null pointer to an // initialized instance. let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() }; request_fd().unwrap_or(-1) } + +unsafe extern "C" fn param_delete_fd_wrapper(param: *mut c_void) { + // SAFETY: This is only ever called by RpcPreconnectedClient, with param being the + // pointer returned from Box::into_raw. + let request_fd_box = unsafe { Box::from_raw(param as *mut Box<dyn FnMut() -> Option<RawFd>>) }; + drop(request_fd_box); +} diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 8c0501ba2f..771c65bf92 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -1160,6 +1160,12 @@ macro_rules! declare_binder_enum { pub const fn enum_values() -> [Self; $size] { [$(Self::$name),*] } + + #[inline(always)] + #[allow(missing_docs)] + pub const fn get(&self) -> $backing { + self.0 + } } impl std::fmt::Debug for $enum { @@ -1201,5 +1207,45 @@ macro_rules! declare_binder_enum { Ok(v.map(|v| v.into_iter().map(Self).collect())) } } + + impl std::ops::BitOr for $enum { + type Output = Self; + fn bitor(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } + } + + impl std::ops::BitOrAssign for $enum { + fn bitor_assign(&mut self, rhs: Self) { + self.0 = self.0 | rhs.0; + } + } + + impl std::ops::BitAnd for $enum { + type Output = Self; + fn bitand(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } + } + + impl std::ops::BitAndAssign for $enum { + fn bitand_assign(&mut self, rhs: Self) { + self.0 = self.0 & rhs.0; + } + } + + impl std::ops::BitXor for $enum { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self { + Self(self.0 ^ rhs.0) + } + } + + impl std::ops::BitXorAssign for $enum { + fn bitxor_assign(&mut self, rhs: Self) { + self.0 = self.0 ^ rhs.0; + } + } + }; } diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index c0cac830d7..609334eb53 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -28,8 +28,9 @@ impl ProcessState { /// `num_threads` additional threads as specified by /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count). /// - /// This should be done before creating any Binder client or server. If - /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are + /// If this is called, it must be done before creating any Binder client or server. + /// + /// If neither this nor [`join_thread_pool`](Self::join_thread_pool) are /// called, then some things (such as callbacks and /// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently /// not work: the callbacks will be queued but never called as there is no diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 170b2adfbb..7c9c452c01 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -382,7 +382,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( sockaddr_un addr_un{}; addr_un.sun_family = AF_UNIX; strcpy(addr_un.sun_path, serverConfig.addr.c_str()); - addr = *reinterpret_cast<sockaddr_storage*>(&addr_un); + std::memcpy(&addr, &addr_un, sizeof(sockaddr_un)); addrLen = sizeof(sockaddr_un); status = session->setupPreconnectedClient({}, [=]() { @@ -394,7 +394,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( sockaddr_un addr_un{}; addr_un.sun_family = AF_UNIX; strcpy(addr_un.sun_path, serverConfig.addr.c_str()); - addr = *reinterpret_cast<sockaddr_storage*>(&addr_un); + std::memcpy(&addr, &addr_un, sizeof(sockaddr_un)); addrLen = sizeof(sockaddr_un); status = session->setupUnixDomainClient(serverConfig.addr.c_str()); @@ -409,7 +409,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( .svm_port = static_cast<unsigned int>(serverInfo.port), .svm_cid = VMADDR_CID_LOCAL, }; - addr = *reinterpret_cast<sockaddr_storage*>(&addr_vm); + std::memcpy(&addr, &addr_vm, sizeof(sockaddr_vm)); addrLen = sizeof(sockaddr_vm); status = session->setupVsockClient(VMADDR_CID_LOCAL, serverInfo.port); @@ -420,7 +420,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( addr_in.sin_family = AF_INET; addr_in.sin_port = htons(serverInfo.port); inet_aton(ip_addr.c_str(), &addr_in.sin_addr); - addr = *reinterpret_cast<sockaddr_storage*>(&addr_in); + std::memcpy(&addr, &addr_in, sizeof(sockaddr_in)); addrLen = sizeof(sockaddr_in); status = session->setupInetClient(ip_addr.c_str(), serverInfo.port); diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index cac054eb1b..457eaa5a76 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -109,6 +109,9 @@ cc_library_static { "libcutils", "libutils", ], + header_libs: [ + "libaidl_transactions", + ], local_include_dirs: ["include_random_parcel"], export_include_dirs: ["include_random_parcel"], } diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index 02e69cc371..11aa76891b 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#include <aidl/transaction_ids.h> #include <fuzzbinder/libbinder_driver.h> #include <fuzzbinder/random_parcel.h> @@ -31,6 +33,28 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider)); } +uint32_t getCode(FuzzedDataProvider& provider) { + if (provider.ConsumeBool()) { + return provider.ConsumeIntegral<uint32_t>(); + } + + // Most of the AIDL services will have small set of transaction codes. + if (provider.ConsumeBool()) { + return provider.ConsumeIntegralInRange<uint32_t>(0, 100); + } + + if (provider.ConsumeBool()) { + return provider.PickValueInArray<uint32_t>( + {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION, + IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION, + IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION, + IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION}); + } + + return provider.ConsumeIntegralInRange<uint32_t>(aidl::kLastMetaMethodId, + aidl::kFirstMetaMethodId); +} + void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) { RandomParcelOptions options{ .extraBinders = binders, @@ -61,16 +85,7 @@ void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& p } while (provider.remaining_bytes() > 0) { - // Most of the AIDL services will have small set of transaction codes. - // TODO(b/295942369) : Add remaining transact codes from IBinder.h - uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>() - : provider.ConsumeBool() - ? provider.ConsumeIntegralInRange<uint32_t>(0, 100) - : provider.PickValueInArray<uint32_t>( - {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION, - IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION, - IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION, - IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION}); + uint32_t code = getCode(provider); uint32_t flags = provider.ConsumeIntegral<uint32_t>(); Parcel data; // for increased fuzz coverage diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h index 583ad015e1..1ac00ca13f 100644 --- a/libs/binder/trusty/include/binder/RpcServerTrusty.h +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -94,9 +94,17 @@ private: static sp<RpcServer> makeRpcServer(std::unique_ptr<RpcTransportCtx> ctx) { auto rpcServer = sp<RpcServer>::make(std::move(ctx)); - // TODO(b/266741352): follow-up to prevent needing this in the future - // Trusty needs to be set to the latest stable version that is in prebuilts there. - LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(0)); + // By default we use the latest stable version. + LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION)); + + // The default behavior in trusty is to allow handles to be passed with tipc IPC. + // We add mode NONE so that servers do not reject connections from clients who do + // not change their default transport mode. + static const std::vector<RpcSession::FileDescriptorTransportMode> + TRUSTY_SERVER_SUPPORTED_FD_MODES = {RpcSession::FileDescriptorTransportMode::TRUSTY, + RpcSession::FileDescriptorTransportMode::NONE}; + + rpcServer->setSupportedFileDescriptorTransportModes(TRUSTY_SERVER_SUPPORTED_FD_MODES); return rpcServer; } diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk index ef1b7c3cf8..40fc21867d 100644 --- a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk +++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk @@ -30,6 +30,8 @@ MODULE_LIBRARY_DEPS += \ trusty/user/base/lib/libstdc++-trusty \ trusty/user/base/lib/trusty-sys \ +MODULE_SRCDEPS := $(LIBBINDER_DIR)/include_rpc_unstable/binder_rpc_unstable.hpp + MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp MODULE_BINDGEN_FLAGS += \ diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index a9bd11e41d..43ee33eb86 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -101,7 +101,7 @@ static const std::vector<std::string> aidl_interfaces_to_dump { "android.hardware.automotive.remoteaccess.IRemoteAccess", "android.hardware.automotive.vehicle.IVehicle", "android.hardware.biometrics.face.IBiometricsFace", - "android.hardware.biometrics.fingerprint.IBiometricsFingerprint", + "android.hardware.biometrics.fingerprint.IFingerprint", "android.hardware.camera.provider.ICameraProvider", "android.hardware.drm.IDrmFactory", "android.hardware.graphics.allocator.IAllocator", diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 08ce855c2b..5244442ff3 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -26,6 +26,7 @@ cc_test { "function_test.cpp", "future_test.cpp", "hash_test.cpp", + "ignore_test.cpp", "match_test.cpp", "mixins_test.cpp", "non_null_test.cpp", diff --git a/libs/ftl/ignore_test.cpp b/libs/ftl/ignore_test.cpp new file mode 100644 index 0000000000..5d5c67b8b1 --- /dev/null +++ b/libs/ftl/ignore_test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2025 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 <string> + +#include <ftl/ignore.h> +#include <gtest/gtest.h> + +namespace android::test { +namespace { + +// Keep in sync with the example usage in the header file. + +void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) { + // When invoked, all the arguments are ignored. + ftl::ignore(arg1, arg2, arg3); +} + +void ftl_ignore_single(int arg) { + // It can be used like std::ignore to ignore a single value + ftl::ignore = arg; +} + +} // namespace + +TEST(Ignore, Example) { + // The real example test is that there are no compiler warnings for unused arguments above. + + // Use the example functions to avoid a compiler warning about unused functions. + ftl_ignore_multiple(0, "a", "b"); + ftl_ignore_single(0); +} + +} // namespace android::test diff --git a/libs/graphicsenv/FeatureOverrides.cpp b/libs/graphicsenv/FeatureOverrides.cpp index 6974da9934..9e7a4cf4a1 100644 --- a/libs/graphicsenv/FeatureOverrides.cpp +++ b/libs/graphicsenv/FeatureOverrides.cpp @@ -14,22 +14,179 @@ * limitations under the License. */ -#include <graphicsenv/FeatureOverrides.h> +#include <cinttypes> #include <android-base/stringprintf.h> +#include <binder/Parcel.h> +#include <graphicsenv/FeatureOverrides.h> namespace android { using base::StringAppendF; +status_t FeatureConfig::writeToParcel(Parcel* parcel) const { + status_t status; + + status = parcel->writeUtf8AsUtf16(mFeatureName); + if (status != OK) { + return status; + } + status = parcel->writeBool(mEnabled); + if (status != OK) { + return status; + } + // Number of GPU vendor IDs. + status = parcel->writeVectorSize(mGpuVendorIDs); + if (status != OK) { + return status; + } + // GPU vendor IDs. + for (const auto& vendorID : mGpuVendorIDs) { + status = parcel->writeUint32(vendorID); + if (status != OK) { + return status; + } + } + + return OK; +} + +status_t FeatureConfig::readFromParcel(const Parcel* parcel) { + status_t status; + + status = parcel->readUtf8FromUtf16(&mFeatureName); + if (status != OK) { + return status; + } + status = parcel->readBool(&mEnabled); + if (status != OK) { + return status; + } + // Number of GPU vendor IDs. + int numGpuVendorIDs; + status = parcel->readInt32(&numGpuVendorIDs); + if (status != OK) { + return status; + } + // GPU vendor IDs. + for (int i = 0; i < numGpuVendorIDs; i++) { + uint32_t gpuVendorIdUint; + status = parcel->readUint32(&gpuVendorIdUint); + if (status != OK) { + return status; + } + mGpuVendorIDs.emplace_back(gpuVendorIdUint); + } + + return OK; +} + std::string FeatureConfig::toString() const { std::string result; StringAppendF(&result, "Feature: %s\n", mFeatureName.c_str()); StringAppendF(&result, " Status: %s\n", mEnabled ? "enabled" : "disabled"); + for (const auto& vendorID : mGpuVendorIDs) { + // vkjson outputs decimal, so print both formats. + StringAppendF(&result, " GPU Vendor ID: 0x%04X (%d)\n", vendorID, vendorID); + } return result; } +status_t FeatureOverrides::writeToParcel(Parcel* parcel) const { + status_t status; + // Number of global feature configs. + status = parcel->writeVectorSize(mGlobalFeatures); + if (status != OK) { + return status; + } + // Global feature configs. + for (const auto& cfg : mGlobalFeatures) { + status = cfg.writeToParcel(parcel); + if (status != OK) { + return status; + } + } + // Number of package feature overrides. + status = parcel->writeInt32(static_cast<int32_t>(mPackageFeatures.size())); + if (status != OK) { + return status; + } + for (const auto& feature : mPackageFeatures) { + // Package name. + status = parcel->writeUtf8AsUtf16(feature.first); + if (status != OK) { + return status; + } + // Number of package feature configs. + status = parcel->writeVectorSize(feature.second); + if (status != OK) { + return status; + } + // Package feature configs. + for (const auto& cfg : feature.second) { + status = cfg.writeToParcel(parcel); + if (status != OK) { + return status; + } + } + } + + return OK; +} + +status_t FeatureOverrides::readFromParcel(const Parcel* parcel) { + status_t status; + + // Number of global feature configs. + status = parcel->resizeOutVector(&mGlobalFeatures); + if (status != OK) { + return status; + } + // Global feature configs. + for (FeatureConfig& cfg : mGlobalFeatures) { + status = cfg.readFromParcel(parcel); + if (status != OK) { + return status; + } + } + + // Number of package feature overrides. + int numPkgOverrides; + status = parcel->readInt32(&numPkgOverrides); + if (status != OK) { + return status; + } + // Package feature overrides. + for (int i = 0; i < numPkgOverrides; i++) { + // Package name. + std::string name; + status = parcel->readUtf8FromUtf16(&name); + if (status != OK) { + return status; + } + std::vector<FeatureConfig> cfgs; + // Number of package feature configs. + int numCfgs; + status = parcel->readInt32(&numCfgs); + if (status != OK) { + return status; + } + // Package feature configs. + for (int j = 0; j < numCfgs; j++) { + FeatureConfig cfg; + status = cfg.readFromParcel(parcel); + if (status != OK) { + return status; + } + cfgs.emplace_back(cfg); + } + mPackageFeatures[name] = cfgs; + } + + return OK; +} + std::string FeatureOverrides::toString() const { std::string result; result.append("Global Features:\n"); diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 4bc261106a..03e6456ea6 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -621,6 +621,10 @@ void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNati mShouldUseAngle = true; } mShouldUseNativeDriver = shouldUseNativeDriver; + + if (mShouldUseAngle) { + updateAngleFeatureOverrides(); + } } std::string& GraphicsEnv::getPackageName() { @@ -632,9 +636,25 @@ const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() { return mAngleEglFeatures; } +// List of ANGLE features to override (enabled or disable). +// The list of overrides is loaded and parsed by GpuService. +void GraphicsEnv::updateAngleFeatureOverrides() { + if (!graphicsenv_flags::angle_feature_overrides()) { + return; + } + + const sp<IGpuService> gpuService = getGpuService(); + if (!gpuService) { + ALOGE("No GPU service"); + return; + } + + mFeatureOverrides = gpuService->getFeatureOverrides(); +} + void GraphicsEnv::getAngleFeatureOverrides(std::vector<const char*>& enabled, std::vector<const char*>& disabled) { - if (!graphicsenv_flags::feature_overrides()) { + if (!graphicsenv_flags::angle_feature_overrides()) { return; } diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp index 42e7c378a9..9a34aff299 100644 --- a/libs/graphicsenv/IGpuService.cpp +++ b/libs/graphicsenv/IGpuService.cpp @@ -119,6 +119,21 @@ public: } return driverPath; } + + FeatureOverrides getFeatureOverrides() override { + Parcel data, reply; + data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); + + FeatureOverrides featureOverrides; + status_t error = + remote()->transact(BnGpuService::GET_FEATURE_CONFIG_OVERRIDES, data, &reply); + if (error != OK) { + return featureOverrides; + } + + featureOverrides.readFromParcel(&reply); + return featureOverrides; + } }; IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService"); @@ -271,6 +286,15 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep toggleAngleAsSystemDriver(enableAngleAsSystemDriver); return OK; } + case GET_FEATURE_CONFIG_OVERRIDES: { + CHECK_INTERFACE(IGpuService, data, reply); + + // Get the FeatureOverrides from gpuservice, which implements the IGpuService interface + // with GpuService::getFeatureOverrides(). + FeatureOverrides featureOverrides = getFeatureOverrides(); + featureOverrides.writeToParcel(reply); + return OK; + } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/graphicsenv/graphicsenv_flags.aconfig b/libs/graphicsenv/graphicsenv_flags.aconfig index ac66362242..efa4bca892 100644 --- a/libs/graphicsenv/graphicsenv_flags.aconfig +++ b/libs/graphicsenv/graphicsenv_flags.aconfig @@ -2,8 +2,8 @@ package: "com.android.graphics.graphicsenv.flags" container: "system" flag { - name: "feature_overrides" - namespace: "core_graphics" - description: "This flag controls the Feature Overrides in GraphicsEnv." + name: "angle_feature_overrides" + namespace: "gpu" + description: "This flag controls the ANGLE Feature Overrides in GraphicsEnv." bug: "372694741" } diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h index 2b9418733d..5dc613b901 100644 --- a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h +++ b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h @@ -20,28 +20,35 @@ #include <string> #include <vector> +#include <binder/Parcelable.h> + namespace android { -class FeatureConfig { +class FeatureConfig : public Parcelable { public: FeatureConfig() = default; FeatureConfig(const FeatureConfig&) = default; virtual ~FeatureConfig() = default; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); std::string toString() const; std::string mFeatureName; bool mEnabled; + std::vector<uint32_t> mGpuVendorIDs; }; /* * Class for transporting OpenGL ES Feature configurations from GpuService to authorized * recipients. */ -class FeatureOverrides { +class FeatureOverrides : public Parcelable { public: FeatureOverrides() = default; FeatureOverrides(const FeatureOverrides&) = default; virtual ~FeatureOverrides() = default; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); std::string toString() const; std::vector<FeatureConfig> mGlobalFeatures; diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 55fa13abb5..68219008e8 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -121,6 +121,7 @@ public: // Get the app package name. std::string& getPackageName(); const std::vector<std::string>& getAngleEglFeatures(); + void updateAngleFeatureOverrides(); void getAngleFeatureOverrides(std::vector<const char*>& enabled, std::vector<const char*>& disabled); // Set the persist.graphics.egl system property value. diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h index a0d6e37302..442683a3e9 100644 --- a/libs/graphicsenv/include/graphicsenv/IGpuService.h +++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h @@ -18,6 +18,7 @@ #include <binder/IInterface.h> #include <cutils/compiler.h> +#include <graphicsenv/FeatureOverrides.h> #include <graphicsenv/GpuStatsInfo.h> #include <vector> @@ -55,6 +56,9 @@ public: // sets ANGLE as system GLES driver if enabled==true by setting persist.graphics.egl to true. virtual void toggleAngleAsSystemDriver(bool enabled) = 0; + + // Get the list of features to override. + virtual FeatureOverrides getFeatureOverrides() = 0; }; class BnGpuService : public BnInterface<IGpuService> { @@ -67,6 +71,7 @@ public: TOGGLE_ANGLE_AS_SYSTEM_DRIVER, SET_TARGET_STATS_ARRAY, ADD_VULKAN_ENGINE_NAME, + GET_FEATURE_CONFIG_OVERRIDES, // Always append new enum to the end. }; diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 5ab31dbaba..158c54886d 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -281,6 +281,7 @@ filegroup { "SurfaceControl.cpp", "SurfaceComposerClient.cpp", "SyncFeatures.cpp", + "TransactionState.cpp", "VsyncEventData.cpp", "view/Surface.cpp", "WindowInfosListenerReporter.cpp", @@ -294,7 +295,6 @@ filegroup { cc_defaults { name: "libgui-defaults", defaults: ["libgui_bufferqueue-defaults"], - srcs: [":libgui-sources"], static_libs: [ "libgui_aidl_static", "libgui_window_info_static", @@ -318,6 +318,10 @@ cc_library_shared { "libgui-defaults", ], + srcs: [ + ":libgui-sources", + ], + export_static_lib_headers: [ "libgui_aidl_static", "libgui_window_info_static", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index c770db9cf3..1aae13c1f4 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -197,15 +197,15 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati mUpdateDestinationFrame(updateDestinationFrame) { createBufferQueue(&mProducer, &mConsumer); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer, - GraphicBuffer::USAGE_HW_COMPOSER | - GraphicBuffer::USAGE_HW_TEXTURE, - 1, false, this); + mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mProducer, mConsumer, + GraphicBuffer::USAGE_HW_COMPOSER | + GraphicBuffer::USAGE_HW_TEXTURE, + 1, false, this); #else - mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer, - GraphicBuffer::USAGE_HW_COMPOSER | - GraphicBuffer::USAGE_HW_TEXTURE, - 1, false, this); + mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mConsumer, + GraphicBuffer::USAGE_HW_COMPOSER | + GraphicBuffer::USAGE_HW_TEXTURE, + 1, false, this); #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // since the adapter is in the client process, set dequeue timeout // explicitly so that dequeueBuffer will block @@ -222,8 +222,6 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers); mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers); mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers; - mNumAcquired = 0; - mNumFrameAvailable = 0; TransactionCompletedListener::getInstance()->addQueueStallListener( [&](const std::string& reason) { @@ -439,7 +437,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence void BLASTBufferQueue::flushShadowQueue() { BQA_LOGV("flushShadowQueue"); - int numFramesToFlush = mNumFrameAvailable; + int32_t numFramesToFlush = mNumFrameAvailable; while (numFramesToFlush > 0) { acquireNextBufferLocked(std::nullopt); numFramesToFlush--; @@ -639,7 +637,8 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mScalingMode, crop); auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); - sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; + sp<Fence> fence = + bufferItem.mFence ? sp<Fence>::make(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; { @@ -938,15 +937,22 @@ public: : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {} void allocateBuffers() override { + ATRACE_CALL(); uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth; uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight; auto gbp = getIGraphicBufferProducer(); - std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(), - reqFormat=mReqFormat, reqUsage=mReqUsage] () { + std::thread allocateThread([reqWidth, reqHeight, gbp = getIGraphicBufferProducer(), + reqFormat = mReqFormat, reqUsage = mReqUsage]() { + if (com_android_graphics_libgui_flags_allocate_buffer_priority()) { + androidSetThreadName("allocateBuffers"); + pid_t tid = gettid(); + androidSetThreadPriority(tid, ANDROID_PRIORITY_DISPLAY); + } + gbp->allocateBuffers(reqWidth, reqHeight, reqFormat, reqUsage); - - }).detach(); + }); + allocateThread.detach(); } status_t setFrameRate(float frameRate, int8_t compatibility, @@ -1016,7 +1022,7 @@ sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) { if (includeSurfaceControlHandle && mSurfaceControl) { scHandle = mSurfaceControl->getHandle(); } - return new BBQSurface(mProducer, true, scHandle, this); + return sp<BBQSurface>::make(mProducer, true, scHandle, this); } void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, @@ -1122,10 +1128,10 @@ ANDROID_SINGLETON_STATIC_INSTANCE(AsyncWorker); class AsyncProducerListener : public BnProducerListener { private: const sp<IProducerListener> mListener; - -public: AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {} + friend class sp<AsyncProducerListener>; +public: void onBufferReleased() override { AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); }); } @@ -1179,7 +1185,7 @@ public: return BufferQueueProducer::connect(listener, api, producerControlledByApp, output); } - return BufferQueueProducer::connect(new AsyncProducerListener(listener), api, + return BufferQueueProducer::connect(sp<AsyncProducerListener>::make(listener), api, producerControlledByApp, output); } diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index 3b2d337a21..9dcd5dc4c5 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -215,14 +215,14 @@ status_t BufferItem::unflatten( FlattenableUtils::read(buffer, size, flags); if (flags & 1) { - mGraphicBuffer = new GraphicBuffer(); + mGraphicBuffer = sp<GraphicBuffer>::make(); status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); } if (flags & 2) { - mFence = new Fence(); + mFence = sp<Fence>::make(); status_t err = mFence->unflatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 8566419435..4926ceb619 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "BufferItemConsumer" //#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Errors.h> #include <utils/Log.h> #include <inttypes.h> @@ -24,6 +25,7 @@ #include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> +#include <gui/Surface.h> #include <ui/BufferQueueDefs.h> #include <ui/GraphicBuffer.h> @@ -35,6 +37,30 @@ namespace android { +std::tuple<sp<BufferItemConsumer>, sp<Surface>> BufferItemConsumer::create( + uint64_t consumerUsage, int bufferCount, bool controlledByApp, + bool isConsumerSurfaceFlinger) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<BufferItemConsumer> bufferItemConsumer = + sp<BufferItemConsumer>::make(consumerUsage, bufferCount, controlledByApp, + isConsumerSurfaceFlinger); + return {bufferItemConsumer, bufferItemConsumer->getSurface()}; +#else + sp<IGraphicBufferProducer> igbp; + sp<IGraphicBufferConsumer> igbc; + BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger); + sp<BufferItemConsumer> bufferItemConsumer = + sp<BufferItemConsumer>::make(igbc, consumerUsage, bufferCount, controlledByApp); + return {bufferItemConsumer, sp<Surface>::make(igbp, controlledByApp)}; +#endif +} + +sp<BufferItemConsumer> BufferItemConsumer::create(const sp<IGraphicBufferConsumer>& consumer, + uint64_t consumerUsage, int bufferCount, + bool controlledByApp) { + return sp<BufferItemConsumer>::make(consumer, consumerUsage, bufferCount, controlledByApp); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount, bool controlledByApp, bool isConsumerSurfaceFlinger) @@ -107,13 +133,36 @@ status_t BufferItemConsumer::acquireBuffer(BufferItem *item, return OK; } +status_t BufferItemConsumer::attachBuffer(const sp<GraphicBuffer>& buffer) { + if (!buffer) { + BI_LOGE("BufferItemConsumer::attachBuffer no input buffer specified."); + return BAD_VALUE; + } + + Mutex::Autolock _l(mMutex); + + int slot = INVALID_BUFFER_SLOT; + status_t status = mConsumer->attachBuffer(&slot, buffer); + if (status != OK) { + BI_LOGE("BufferItemConsumer::attachBuffer unable to attach buffer %d", status); + return status; + } + + mSlots[slot] = { + .mGraphicBuffer = buffer, + .mFence = nullptr, + .mFrameNumber = 0, + }; + + return OK; +} + status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, const sp<Fence>& releaseFence) { Mutex::Autolock _l(mMutex); return releaseBufferSlotLocked(item.mSlot, item.mGraphicBuffer, releaseFence); } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence) { Mutex::Autolock _l(mMutex); @@ -129,7 +178,6 @@ status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer, return releaseBufferSlotLocked(slotIndex, buffer, releaseFence); } -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence) { diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index f1374e23fd..f21ac18f3c 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -125,15 +125,16 @@ void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); - sp<BufferQueueCore> core(new BufferQueueCore()); + sp<BufferQueueCore> core = sp<BufferQueueCore>::make(); LOG_ALWAYS_FATAL_IF(core == nullptr, "BufferQueue: failed to create BufferQueueCore"); - sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); + sp<IGraphicBufferProducer> producer = + sp<BufferQueueProducer>::make(core, consumerIsSurfaceFlinger); LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); - sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); + sp<IGraphicBufferConsumer> consumer = sp<BufferQueueConsumer>::make(core); LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 270bfbdc64..4681c9ecbe 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -#include <inttypes.h> -#include <pwd.h> -#include <sys/types.h> - #define LOG_TAG "BufferQueueConsumer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 @@ -48,6 +44,11 @@ #include <com_android_graphics_libgui_flags.h> +#include <inttypes.h> +#include <pwd.h> +#include <sys/types.h> +#include <optional> + namespace android { // Macros for include BufferQueueCore information in log messages @@ -767,11 +768,15 @@ status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) { return NO_ERROR; } +status_t BufferQueueConsumer::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { + return setMaxAcquiredBufferCount(maxAcquiredBuffers, std::nullopt); +} + status_t BufferQueueConsumer::setMaxAcquiredBufferCount( - int maxAcquiredBuffers) { + int maxAcquiredBuffers, std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) { ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers); - sp<IConsumerListener> listener; + std::optional<OnBufferReleasedCallback> callback; { // Autolock scope std::unique_lock<std::mutex> lock(mCore->mMutex); @@ -833,13 +838,20 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers); mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; VALIDATE_CONSISTENCY(); - if (delta < 0 && mCore->mBufferReleasedCbEnabled) { - listener = mCore->mConsumerListener; + if (delta < 0) { + if (onBuffersReleasedCallback) { + callback = std::move(onBuffersReleasedCallback); + } else if (mCore->mBufferReleasedCbEnabled) { + callback = [listener = mCore->mConsumerListener]() { + listener->onBuffersReleased(); + }; + } } } + // Call back without lock held - if (listener != nullptr) { - listener->onBuffersReleased(); + if (callback) { + (*callback)(); } return NO_ERROR; diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index c241482827..bcf61e45de 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -364,8 +364,10 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, // Producers are not allowed to dequeue more than // mMaxDequeuedBufferCount buffers. // This check is only done if a buffer has already been queued - if (mCore->mBufferHasBeenQueued && - dequeuedCount >= mCore->mMaxDequeuedBufferCount) { + using namespace com::android::graphics::libgui::flags; + bool flagGatedBufferHasBeenQueued = + bq_always_use_max_dequeued_buffer_count() || mCore->mBufferHasBeenQueued; + if (flagGatedBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) { // Supress error logs when timeout is non-negative. if (mDequeueTimeout < 0) { BQ_LOGE("%s: attempting to exceed the max dequeued buffer " @@ -693,11 +695,11 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou .requestorName = {mConsumerName.c_str(), mConsumerName.size()}, .extras = std::move(tempOptions), }; - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest); + sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest); #else sp<GraphicBuffer> graphicBuffer = - new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage, - {mConsumerName.c_str(), mConsumerName.size()}); + sp<GraphicBuffer>::make(width, height, format, BQ_LAYER_COUNT, usage, + std::string{mConsumerName.c_str(), mConsumerName.size()}); #endif status_t error = graphicBuffer->initCheck(); @@ -1464,7 +1466,7 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, #ifndef NO_BINDER if (IInterface::asBinder(listener)->remoteBinder() != nullptr) { status = IInterface::asBinder(listener)->linkToDeath( - static_cast<IBinder::DeathRecipient*>(this)); + sp<IBinder::DeathRecipient>::fromExisting(this)); if (status != NO_ERROR) { BQ_LOGE("connect: linkToDeath failed: %s (%d)", strerror(-status), status); @@ -1553,8 +1555,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death // notification, but we just ignore it - token->unlinkToDeath( - static_cast<IBinder::DeathRecipient*>(this)); + token->unlinkToDeath(static_cast<IBinder::DeathRecipient*>(this)); } #endif mCore->mSharedBufferSlot = @@ -1685,11 +1686,11 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, #endif #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest); + sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest); #else - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( - allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, - allocUsage, allocName); + sp<GraphicBuffer> graphicBuffer = + sp<GraphicBuffer>::make(allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, + allocUsage, allocName); #endif status_t result = graphicBuffer->initCheck(); diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp index e9cb013baf..4f495d039d 100644 --- a/libs/gui/BufferReleaseChannel.cpp +++ b/libs/gui/BufferReleaseChannel.cpp @@ -108,7 +108,7 @@ status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count) { - releaseFence = new Fence(); + releaseFence = sp<Fence>::make(); if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) { return err; } @@ -344,4 +344,4 @@ status_t BufferReleaseChannel::open(std::string name, return STATUS_OK; } -} // namespace android::gui
\ No newline at end of file +} // namespace android::gui diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index ab747b9232..80a35435cb 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -20,6 +20,7 @@ #include <gui/Choreographer.h> #include <gui/TraceUtils.h> #include <jni.h> +#include <utils/Looper.h> #undef LOG_TAG #define LOG_TAG "AChoreographer" @@ -69,7 +70,7 @@ namespace android { Choreographer::Context Choreographer::gChoreographers; -static thread_local Choreographer* gChoreographer; +static thread_local sp<Choreographer> gChoreographer; void Choreographer::initJVM(JNIEnv* env) { env->GetJavaVM(&gJni.jvm); @@ -86,14 +87,14 @@ void Choreographer::initJVM(JNIEnv* env) { "()V"); } -Choreographer* Choreographer::getForThread() { +sp<Choreographer> Choreographer::getForThread() { if (gChoreographer == nullptr) { sp<Looper> looper = Looper::getForThread(); if (!looper.get()) { ALOGW("No looper prepared for thread"); return nullptr; } - gChoreographer = new Choreographer(looper); + gChoreographer = sp<Choreographer>::make(looper); status_t result = gChoreographer->initialize(); if (result != OK) { ALOGW("Failed to initialize"); diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 67de742161..5b89c6e17e 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -31,6 +31,7 @@ #include <gui/BufferItem.h> #include <gui/BufferQueue.h> #include <gui/ConsumerBase.h> +#include <gui/IConsumerListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -68,8 +69,8 @@ ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool c #endif mAbandoned(false), mConsumer(bufferQueue), - mPrevFinalReleaseFence(Fence::NO_FENCE) { - initialize(controlledByApp); + mPrevFinalReleaseFence(Fence::NO_FENCE), + mIsControlledByApp(controlledByApp) { } #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) @@ -79,11 +80,11 @@ ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger) mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), #endif mAbandoned(false), - mPrevFinalReleaseFence(Fence::NO_FENCE) { + mPrevFinalReleaseFence(Fence::NO_FENCE), + mIsControlledByApp(controlledByApp) { sp<IGraphicBufferProducer> producer; BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger); mSurface = sp<Surface>::make(producer, controlledByApp); - initialize(controlledByApp); } ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer, @@ -95,24 +96,27 @@ ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer, mAbandoned(false), mConsumer(consumer), mSurface(sp<Surface>::make(producer, controlledByApp)), - mPrevFinalReleaseFence(Fence::NO_FENCE) { - initialize(controlledByApp); + mPrevFinalReleaseFence(Fence::NO_FENCE), + mIsControlledByApp(controlledByApp) { } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) -void ConsumerBase::initialize(bool controlledByApp) { +void ConsumerBase::onFirstRef() { + ConsumerListener::onFirstRef(); + initialize(); +} + +void ConsumerBase::initialize() { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); - // Note that we can't create an sp<...>(this) in a ctor that will not keep a - // reference once the ctor ends, as that would cause the refcount of 'this' - // dropping to 0 at the end of the ctor. Since all we need is a wp<...> - // that's what we create. - wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this); - sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener); + // Here we depend on an sp/wp having been created for `this`. For this + // reason, initialize() cannot be called from a ctor. + wp<ConsumerListener> listener = wp<ConsumerListener>::fromExisting(this); + sp<IConsumerListener> proxy = sp<BufferQueue::ProxyConsumerListener>::make(listener); - status_t err = mConsumer->consumerConnect(proxy, controlledByApp); + status_t err = mConsumer->consumerConnect(proxy, mIsControlledByApp); if (err != NO_ERROR) { CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)", strerror(-err), err); @@ -260,7 +264,10 @@ void ConsumerBase::onFrameReplaced(const BufferItem &item) { void ConsumerBase::onBuffersReleased() { Mutex::Autolock lock(mMutex); + onBuffersReleasedLocked(); +} +void ConsumerBase::onBuffersReleasedLocked() { CB_LOGV("onBuffersReleased"); if (mAbandoned) { @@ -477,10 +484,10 @@ status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { CB_LOGE("setMaxAcquiredBufferCount: ConsumerBase is abandoned!"); return NO_INIT; } - return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers); + return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers, + {[this]() { onBuffersReleasedLocked(); }}); } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) status_t ConsumerBase::setConsumerIsProtected(bool isProtected) { Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -489,7 +496,6 @@ status_t ConsumerBase::setConsumerIsProtected(bool isProtected) { } return mConsumer->setConsumerIsProtected(isProtected); } -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) sp<NativeHandle> ConsumerBase::getSidebandStream() const { Mutex::Autolock _l(mMutex); diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 23b432e1f4..ecbceb7a79 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -20,7 +20,11 @@ #include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> +#include <gui/BufferQueue.h> #include <gui/CpuConsumer.h> +#include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/Surface.h> #include <utils/Log.h> #define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) @@ -31,6 +35,28 @@ namespace android { +std::tuple<sp<CpuConsumer>, sp<Surface>> CpuConsumer::create(size_t maxLockedBuffers, + bool controlledByApp, + bool isConsumerSurfaceFlinger) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<CpuConsumer> consumer = + sp<CpuConsumer>::make(maxLockedBuffers, controlledByApp, isConsumerSurfaceFlinger); + return {consumer, consumer->getSurface()}; +#else + sp<IGraphicBufferProducer> igbp; + sp<IGraphicBufferConsumer> igbc; + BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger); + + return {sp<CpuConsumer>::make(igbc, maxLockedBuffers, controlledByApp), + sp<Surface>::make(igbp, controlledByApp)}; +#endif +} + +sp<CpuConsumer> CpuConsumer::create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, + bool controlledByApp) { + return sp<CpuConsumer>::make(bq, maxLockedBuffers, controlledByApp); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp, bool isConsumerSurfaceFlinger) @@ -230,7 +256,7 @@ status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) { return err; } - sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE); addReleaseFenceLocked(ab.mSlot, ab.mGraphicBuffer, fence); releaseBufferLocked(ab.mSlot, ab.mGraphicBuffer); diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 052b8edfaa..2c5770d4c6 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -37,6 +37,7 @@ #include <gui/DebugEGLImageTracker.h> #include <gui/GLConsumer.h> #include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> @@ -101,6 +102,50 @@ static bool hasEglProtectedContent() { return hasIt; } +std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t tex, uint32_t textureTarget, + bool useFenceSync, + bool isControlledByApp) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<GLConsumer> consumer = + sp<GLConsumer>::make(tex, textureTarget, useFenceSync, isControlledByApp); + return {consumer, consumer->getSurface()}; +#else + sp<IGraphicBufferProducer> igbp; + sp<IGraphicBufferConsumer> igbc; + BufferQueue::createBufferQueue(&igbp, &igbc); + + return {sp<GLConsumer>::make(igbc, tex, textureTarget, useFenceSync, isControlledByApp), + sp<Surface>::make(igbp, isControlledByApp)}; +#endif +} + +std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t textureTarget, + bool useFenceSync, + bool isControlledByApp) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<GLConsumer> consumer = sp<GLConsumer>::make(textureTarget, useFenceSync, isControlledByApp); + return {consumer, consumer->getSurface()}; +#else + sp<IGraphicBufferProducer> igbp; + sp<IGraphicBufferConsumer> igbc; + BufferQueue::createBufferQueue(&igbp, &igbc); + + return {sp<GLConsumer>::make(igbc, textureTarget, useFenceSync, isControlledByApp), + sp<Surface>::make(igbp, isControlledByApp)}; +#endif +} + +sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, + uint32_t textureTarget, bool useFenceSync, + bool isControlledByApp) { + return sp<GLConsumer>::make(bq, tex, textureTarget, useFenceSync, isControlledByApp); +} + +sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, + bool useFenceSync, bool isControlledByApp) { + return sp<GLConsumer>::make(bq, textureTarget, useFenceSync, isControlledByApp); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false), @@ -333,7 +378,7 @@ status_t GLConsumer::releaseTexImage() { } if (mReleasedTexImage == nullptr) { - mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); + mReleasedTexImage = sp<EglImage>::make(getDebugTexImageBuffer()); } mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; @@ -365,10 +410,10 @@ sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { // The first time, create the debug texture in case the application // continues to use it. - sp<GraphicBuffer> buffer = new GraphicBuffer( - kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY, - "[GLConsumer debug texture]"); + sp<GraphicBuffer> buffer = + sp<GraphicBuffer>::make(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY, + "[GLConsumer debug texture]"); uint32_t* bits; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); uint32_t stride = buffer->getStride(); @@ -400,7 +445,7 @@ status_t GLConsumer::acquireBufferLocked(BufferItem *item, // replaces any old EglImage with a new one (using the new buffer). if (item->mGraphicBuffer != nullptr) { int slot = item->mSlot; - mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); + mEglSlots[slot].mEglImage = sp<EglImage>::make(item->mGraphicBuffer); } return NO_ERROR; @@ -737,7 +782,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { "fd: %#x", eglGetError()); return UNKNOWN_ERROR; } - sp<Fence> fence(new Fence(fenceFd)); + sp<Fence> fence = sp<Fence>::make(fenceFd); status_t err = addReleaseFenceLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer(), fence); if (err != OK) { diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 9f71eb16e7..1d1910eb08 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -104,7 +104,7 @@ public: } bool nonNull = reply.readInt32(); if (nonNull) { - *buf = new GraphicBuffer(); + *buf = sp<GraphicBuffer>::make(); result = reply.read(**buf); if(result != NO_ERROR) { (*buf).clear(); @@ -197,7 +197,7 @@ public: } *buf = reply.readInt32(); - *fence = new Fence(); + *fence = sp<Fence>::make(); result = reply.read(**fence); if (result != NO_ERROR) { fence->clear(); @@ -293,7 +293,7 @@ public: if (result == NO_ERROR) { bool nonNull = reply.readInt32(); if (nonNull) { - *outBuffer = new GraphicBuffer; + *outBuffer = sp<GraphicBuffer>::make(); result = reply.read(**outBuffer); if (result != NO_ERROR) { outBuffer->clear(); @@ -302,7 +302,7 @@ public: } nonNull = reply.readInt32(); if (nonNull) { - *outFence = new Fence; + *outFence = sp<Fence>::make(); result = reply.read(**outFence); if (result != NO_ERROR) { outBuffer->clear(); @@ -640,7 +640,7 @@ public: bool hasBuffer = reply.readBool(); sp<GraphicBuffer> buffer; if (hasBuffer) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); result = reply.read(*buffer); if (result == NO_ERROR) { result = reply.read(outTransformMatrix, sizeof(float) * 16); @@ -650,7 +650,7 @@ public: ALOGE("getLastQueuedBuffer failed to read buffer: %d", result); return result; } - sp<Fence> fence(new Fence); + sp<Fence> fence = sp<Fence>::make(); result = reply.read(*fence); if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to read fence: %d", result); @@ -687,7 +687,7 @@ public: } sp<GraphicBuffer> buffer; if (hasBuffer) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); result = reply.read(*buffer); if (result == NO_ERROR) { result = reply.read(*outRect); @@ -700,7 +700,7 @@ public: ALOGE("getLastQueuedBuffer failed to read buffer: %d", result); return result; } - sp<Fence> fence(new Fence); + sp<Fence> fence = sp<Fence>::make(); result = reply.read(*fence); if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to read fence: %d", result); @@ -1232,7 +1232,7 @@ status_t BnGraphicBufferProducer::onTransact( } case ATTACH_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - sp<GraphicBuffer> buffer = new GraphicBuffer(); + sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(); status_t result = data.read(*buffer.get()); int slot = 0; if (result == NO_ERROR) { @@ -1250,7 +1250,7 @@ status_t BnGraphicBufferProducer::onTransact( return result; } for (sp<GraphicBuffer>& buffer : buffers) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); result = data.read(*buffer.get()); if (result != NO_ERROR) { return result; @@ -1306,7 +1306,7 @@ status_t BnGraphicBufferProducer::onTransact( case CANCEL_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); - sp<Fence> fence = new Fence(); + sp<Fence> fence = sp<Fence>::make(); status_t result = data.read(*fence.get()); if (result == NO_ERROR) { result = cancelBuffer(buf, fence); diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp index 8b2e2ddc59..393e1c3068 100644 --- a/libs/gui/IGraphicBufferProducerFlattenables.cpp +++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp @@ -105,7 +105,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( : std::nullopt; #endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES - fence = new Fence(); + fence = sp<Fence>::make(); status_t result = fence->unflatten(buffer, size, fds, count); if (result != NO_ERROR) { return result; @@ -228,7 +228,7 @@ status_t IGraphicBufferProducer::RequestBufferOutput::unflatten( FlattenableUtils::read(fBuffer, size, result); int32_t isBufferNull = 0; FlattenableUtils::read(fBuffer, size, isBufferNull); - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); if (!isBufferNull) { status_t status = buffer->unflatten(fBuffer, size, fds, count); if (status != NO_ERROR) { @@ -323,7 +323,7 @@ status_t IGraphicBufferProducer::DequeueBufferOutput::unflatten( FlattenableUtils::read(buffer, size, slot); FlattenableUtils::read(buffer, size, bufferAge); - fence = new Fence(); + fence = sp<Fence>::make(); status_t status = fence->unflatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; @@ -395,7 +395,7 @@ status_t IGraphicBufferProducer::CancelBufferInput::unflatten( FlattenableUtils::read(buffer, size, slot); - fence = new Fence(); + fence = sp<Fence>::make(); return fence->unflatten(buffer, size, fds, count); } diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index 83fc827c5f..ed28e7960b 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -92,7 +92,7 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) { if (err != NO_ERROR) return err; if (hasFence) { - gpuCompositionDoneFence = new Fence(); + gpuCompositionDoneFence = sp<Fence>::make(); err = input->read(*gpuCompositionDoneFence); if (err != NO_ERROR) return err; } @@ -157,7 +157,7 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readBool, &hasFence); if (hasFence) { - previousReleaseFence = new Fence(); + previousReleaseFence = sp<Fence>::make(); SAFE_PARCEL(input->read, *previousReleaseFence); } bool hasTransformHint = false; @@ -216,7 +216,7 @@ status_t TransactionStats::readFromParcel(const Parcel* input) { return err; } if (hasFence) { - presentFence = new Fence(); + presentFence = sp<Fence>::make(); err = input->read(*presentFence); if (err != NO_ERROR) { return err; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index ebfc62f33f..ad95d1a85a 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -55,6 +55,28 @@ namespace android { using gui::FocusRequest; using gui::WindowInfoHandle; +namespace { +bool isSameWindowHandle(const sp<WindowInfoHandle>& lhs, const sp<WindowInfoHandle>& rhs) { + if (lhs == rhs) { + return true; + } + + if (!lhs || !rhs) { + return false; + } + + return *lhs->getInfo() == *rhs->getInfo(); +}; + +bool isSameSurfaceControl(const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) { + if (lhs == rhs) { + return true; + } + + return SurfaceControl::isSameSurface(lhs, rhs); +}; +} // namespace + layer_state_t::layer_state_t() : surface(nullptr), layerId(-1), @@ -73,7 +95,6 @@ layer_state_t::layer_state_t() transformToDisplayInverse(false), crop({0, 0, -1, -1}), dataspace(ui::Dataspace::UNKNOWN), - surfaceDamageRegion(), api(-1), colorTransform(mat4()), bgColor(0), @@ -117,19 +138,21 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, crop.left); SAFE_PARCEL(output.writeFloat, crop.bottom); SAFE_PARCEL(output.writeFloat, crop.right); - SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl); - SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, + mNotDefCmpState.relativeLayerSurfaceControl); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, + mNotDefCmpState.parentSurfaceControlForChild); SAFE_PARCEL(output.writeFloat, color.r); SAFE_PARCEL(output.writeFloat, color.g); SAFE_PARCEL(output.writeFloat, color.b); SAFE_PARCEL(output.writeFloat, color.a); - SAFE_PARCEL(windowInfoHandle->writeToParcel, &output); - SAFE_PARCEL(output.write, transparentRegion); + SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->writeToParcel, &output); + SAFE_PARCEL(output.write, mNotDefCmpState.transparentRegion); SAFE_PARCEL(output.writeUint32, bufferTransform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace)); SAFE_PARCEL(output.write, hdrMetadata); - SAFE_PARCEL(output.write, surfaceDamageRegion); + SAFE_PARCEL(output.write, mNotDefCmpState.surfaceDamageRegion); SAFE_PARCEL(output.writeInt32, api); if (sidebandStream) { @@ -241,8 +264,10 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readFloat, &crop.bottom); SAFE_PARCEL(input.readFloat, &crop.right); - SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl); - SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, + &mNotDefCmpState.relativeLayerSurfaceControl); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, + &mNotDefCmpState.parentSurfaceControlForChild); float tmpFloat = 0; SAFE_PARCEL(input.readFloat, &tmpFloat); @@ -254,9 +279,9 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readFloat, &tmpFloat); color.a = tmpFloat; - SAFE_PARCEL(windowInfoHandle->readFromParcel, &input); + SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->readFromParcel, &input); - SAFE_PARCEL(input.read, transparentRegion); + SAFE_PARCEL(input.read, mNotDefCmpState.transparentRegion); SAFE_PARCEL(input.readUint32, &bufferTransform); SAFE_PARCEL(input.readBool, &transformToDisplayInverse); @@ -265,7 +290,7 @@ status_t layer_state_t::read(const Parcel& input) dataspace = static_cast<ui::Dataspace>(tmpUint32); SAFE_PARCEL(input.read, hdrMetadata); - SAFE_PARCEL(input.read, surfaceDamageRegion); + SAFE_PARCEL(input.read, mNotDefCmpState.surfaceDamageRegion); SAFE_PARCEL(input.readInt32, &api); bool tmpBool = false; @@ -583,7 +608,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eTransparentRegionChanged) { what |= eTransparentRegionChanged; - transparentRegion = other.transparentRegion; + mNotDefCmpState.transparentRegion = other.mNotDefCmpState.transparentRegion; } if (other.what & eFlagsChanged) { what |= eFlagsChanged; @@ -615,11 +640,13 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eRelativeLayerChanged; what &= ~eLayerChanged; z = other.z; - relativeLayerSurfaceControl = other.relativeLayerSurfaceControl; + mNotDefCmpState.relativeLayerSurfaceControl = + other.mNotDefCmpState.relativeLayerSurfaceControl; } if (other.what & eReparent) { what |= eReparent; - parentSurfaceControlForChild = other.parentSurfaceControlForChild; + mNotDefCmpState.parentSurfaceControlForChild = + other.mNotDefCmpState.parentSurfaceControlForChild; } if (other.what & eBufferTransformChanged) { what |= eBufferTransformChanged; @@ -665,7 +692,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eSurfaceDamageRegionChanged) { what |= eSurfaceDamageRegionChanged; - surfaceDamageRegion = other.surfaceDamageRegion; + mNotDefCmpState.surfaceDamageRegion = other.mNotDefCmpState.surfaceDamageRegion; } if (other.what & eApiChanged) { what |= eApiChanged; @@ -684,7 +711,8 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eInputInfoChanged) { what |= eInputInfoChanged; - windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle); + mNotDefCmpState.windowInfoHandle = + sp<WindowInfoHandle>::make(*other.mNotDefCmpState.windowInfoHandle); } if (other.what & eBackgroundColorChanged) { what |= eBackgroundColorChanged; @@ -807,7 +835,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eAlphaChanged, other, color.a); CHECK_DIFF(diff, eMatrixChanged, other, matrix); if (other.what & eTransparentRegionChanged && - (!transparentRegion.hasSameRects(other.transparentRegion))) { + (!mNotDefCmpState.transparentRegion.hasSameRects( + other.mNotDefCmpState.transparentRegion))) { diff |= eTransparentRegionChanged; } if (other.what & eFlagsChanged) { @@ -824,8 +853,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { diff &= ~eLayerChanged; } if (other.what & eReparent && - !SurfaceControl::isSameSurface(parentSurfaceControlForChild, - other.parentSurfaceControlForChild)) { + !SurfaceControl::isSameSurface(mNotDefCmpState.parentSurfaceControlForChild, + other.mNotDefCmpState.parentSurfaceControlForChild)) { diff |= eReparent; } CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform); @@ -839,7 +868,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint); CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata); if (other.what & eSurfaceDamageRegionChanged && - (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) { + (!mNotDefCmpState.surfaceDamageRegion.hasSameRects( + other.mNotDefCmpState.surfaceDamageRegion))) { diff |= eSurfaceDamageRegionChanged; } CHECK_DIFF(diff, eApiChanged, other, api); @@ -901,6 +931,38 @@ status_t layer_state_t::matrix22_t::read(const Parcel& input) { SAFE_PARCEL(input.readFloat, &dsdy); return NO_ERROR; } +void layer_state_t::updateTransparentRegion(const Region& transparentRegion) { + what |= eTransparentRegionChanged; + mNotDefCmpState.transparentRegion = transparentRegion; +} +void layer_state_t::updateSurfaceDamageRegion(const Region& surfaceDamageRegion) { + what |= eSurfaceDamageRegionChanged; + mNotDefCmpState.surfaceDamageRegion = surfaceDamageRegion; +} +void layer_state_t::updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z) { + what |= layer_state_t::eRelativeLayerChanged; + what &= ~layer_state_t::eLayerChanged; + mNotDefCmpState.relativeLayerSurfaceControl = relativeTo; + this->z = z; +} +void layer_state_t::updateParentLayer(const sp<SurfaceControl>& newParent) { + what |= layer_state_t::eReparent; + mNotDefCmpState.parentSurfaceControlForChild = + newParent ? newParent->getParentingLayer() : nullptr; +} +void layer_state_t::updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info) { + what |= eInputInfoChanged; + mNotDefCmpState.windowInfoHandle = std::move(info); +} + +bool layer_state_t::NotDefaultComparableState::operator==( + const NotDefaultComparableState& rhs) const { + return transparentRegion.hasSameRects(rhs.transparentRegion) && + surfaceDamageRegion.hasSameRects(rhs.surfaceDamageRegion) && + isSameWindowHandle(windowInfoHandle, rhs.windowInfoHandle) && + isSameSurfaceControl(relativeLayerSurfaceControl, rhs.relativeLayerSurfaceControl) && + isSameSurfaceControl(parentSurfaceControlForChild, rhs.parentSurfaceControlForChild); +} // ------------------------------- InputWindowCommands ---------------------------------------- @@ -1001,13 +1063,13 @@ status_t BufferData::readFromParcel(const Parcel* input) { bool tmpBool = false; SAFE_PARCEL(input->readBool, &tmpBool); if (tmpBool) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); SAFE_PARCEL(input->read, *buffer); } SAFE_PARCEL(input->readBool, &tmpBool); if (tmpBool) { - acquireFence = new Fence(); + acquireFence = sp<Fence>::make(); SAFE_PARCEL(input->read, *acquireFence); } @@ -1034,8 +1096,8 @@ status_t BufferData::readFromParcel(const Parcel* input) { } status_t TrustedPresentationListener::writeToParcel(Parcel* parcel) const { - SAFE_PARCEL(parcel->writeStrongBinder, callbackInterface); - SAFE_PARCEL(parcel->writeInt32, callbackId); + SAFE_PARCEL(parcel->writeStrongBinder, mState.callbackInterface); + SAFE_PARCEL(parcel->writeInt32, mState.callbackId); return NO_ERROR; } @@ -1043,9 +1105,9 @@ status_t TrustedPresentationListener::readFromParcel(const Parcel* parcel) { sp<IBinder> tmpBinder = nullptr; SAFE_PARCEL(parcel->readNullableStrongBinder, &tmpBinder); if (tmpBinder) { - callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder); + mState.callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder); } - SAFE_PARCEL(parcel->readInt32, &callbackId); + SAFE_PARCEL(parcel->readInt32, &mState.callbackId); return NO_ERROR; } diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp index 2de023e5b2..30b5785b6a 100644 --- a/libs/gui/ScreenCaptureResults.cpp +++ b/libs/gui/ScreenCaptureResults.cpp @@ -18,6 +18,7 @@ #include <private/gui/ParcelUtils.h> #include <ui/FenceResult.h> +#include <ui/GraphicBuffer.h> namespace android::gui { @@ -54,7 +55,7 @@ status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { bool hasGraphicBuffer; SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer); if (hasGraphicBuffer) { - buffer = new GraphicBuffer(); + buffer = sp<GraphicBuffer>::make(); SAFE_PARCEL(parcel->read, *buffer); } @@ -79,7 +80,7 @@ status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { bool hasGainmap; SAFE_PARCEL(parcel->readBool, &hasGainmap); if (hasGainmap) { - optionalGainMap = new GraphicBuffer(); + optionalGainMap = sp<GraphicBuffer>::make(); SAFE_PARCEL(parcel->read, *optionalGainMap); } SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio); diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp index 653b91bcf6..848ae7a154 100644 --- a/libs/gui/StreamSplitter.cpp +++ b/libs/gui/StreamSplitter.cpp @@ -47,7 +47,7 @@ status_t StreamSplitter::createSplitter( return BAD_VALUE; } - sp<StreamSplitter> splitter(new StreamSplitter(inputQueue)); + sp<StreamSplitter> splitter = sp<StreamSplitter>::make(inputQueue); status_t status = splitter->mInput->consumerConnect(splitter, false); if (status == NO_ERROR) { splitter->mInput->setConsumerName(String8("StreamSplitter")); @@ -82,7 +82,7 @@ status_t StreamSplitter::addOutput( Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput queueBufferOutput; - sp<OutputListener> listener(new OutputListener(this, outputQueue)); + sp<OutputListener> listener = sp<OutputListener>::make(this, outputQueue); IInterface::asBinder(outputQueue)->linkToDeath(listener); status_t status = outputQueue->connect(listener, NATIVE_WINDOW_API_CPU, /* producerControlledByApp */ false, &queueBufferOutput); @@ -140,7 +140,7 @@ void StreamSplitter::onFrameAvailable(const BufferItem& /* item */) { // Initialize our reference count for this buffer mBuffers.add(bufferItem.mGraphicBuffer->getId(), - new BufferTracker(bufferItem.mGraphicBuffer)); + sp<BufferTracker>::make(bufferItem.mGraphicBuffer)); IGraphicBufferProducer::QueueBufferInput queueInput( bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp, diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index ec23365e1f..63dcfbcb9b 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -509,7 +509,7 @@ int Surface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, if (result != OK) { return result; } - sp<Fence> fence(new Fence(fenceFd)); + sp<Fence> fence = sp<Fence>::make(fenceFd); int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED"); if (waitResult != OK) { ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", @@ -979,7 +979,7 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, } return OK; } - sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE); mGraphicBufferProducer->cancelBuffer(i, fence); if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) { @@ -1017,7 +1017,7 @@ int Surface::cancelBuffers(const std::vector<BatchBuffer>& buffers) { ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__); badSlotResult = slot; } else { - sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE); cancelBufferInputs[numBuffersCancelled].slot = slot; cancelBufferInputs[numBuffersCancelled++].fence = fence; } @@ -1078,7 +1078,7 @@ void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fen Rect crop(Rect::EMPTY_RECT); mCrop.intersect(Rect(buffer->width, buffer->height), &crop); - sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, static_cast<android_dataspace>(mDataSpace), crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, @@ -2092,7 +2092,7 @@ bool Surface::transformToDisplayInverse() const { } int Surface::connect(int api) { - static sp<SurfaceListener> listener = new StubSurfaceListener(); + static sp<SurfaceListener> listener = sp<StubSurfaceListener>::make(); return connect(api, listener); } @@ -2104,7 +2104,7 @@ int Surface::connect(int api, const sp<SurfaceListener>& listener, bool reportBu mReportRemovedBuffers = reportBufferRemoval; if (listener != nullptr) { - mListenerProxy = new ProducerListenerProxy(this, listener); + mListenerProxy = sp<ProducerListenerProxy>::make(this, listener); } int err = diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 37ed23bd6b..9854274cb1 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -123,7 +123,7 @@ bool ComposerService::connectLocked() { explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { } }; - mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this)); + mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerService*>(this)); IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver); return true; } @@ -170,7 +170,7 @@ bool ComposerServiceAIDL::connectLocked() { explicit DeathObserver(ComposerServiceAIDL& mgr) : mComposerService(mgr) {} }; - mDeathObserver = new DeathObserver(*const_cast<ComposerServiceAIDL*>(this)); + mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerServiceAIDL*>(this)); IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver); return true; } @@ -202,7 +202,7 @@ public: DefaultComposerClient& dc = DefaultComposerClient::getInstance(); Mutex::Autolock _l(dc.mLock); if (dc.mClient == nullptr) { - dc.mClient = new SurfaceComposerClient; + dc.mClient = sp<SurfaceComposerClient>::make(); } return dc.mClient; } @@ -399,7 +399,7 @@ void TransactionCompletedListener::setInstance(const sp<TransactionCompletedList sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() { std::lock_guard<std::mutex> lock(sListenerInstanceMutex); if (sInstance == nullptr) { - sInstance = new TransactionCompletedListener; + sInstance = sp<TransactionCompletedListener>::make(); } return sInstance; } @@ -691,7 +691,7 @@ TransactionCompletedListener::addTrustedPresentationCallback(TrustedPresentation std::scoped_lock<std::mutex> lock(mMutex); mTrustedPresentationCallbacks[id] = std::tuple<TrustedPresentationCallback, void*>(tpc, context); - return new SurfaceComposerClient::PresentationCallbackRAII(this, id); + return sp<SurfaceComposerClient::PresentationCallbackRAII>::make(this, id); } void TransactionCompletedListener::clearTrustedPresentationCallback(int id) { @@ -743,7 +743,7 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId); */ class BufferCache : public Singleton<BufferCache> { public: - BufferCache() : token(new BBinder()) {} + BufferCache() : token(sp<BBinder>::make()) {} sp<IBinder> getToken() { return IInterface::asBinder(TransactionCompletedListener::getIInstance()); @@ -1357,7 +1357,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay return binderStatus; } -sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder(); +sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = sp<BBinder>::make(); std::mutex SurfaceComposerClient::Transaction::sApplyTokenMutex; @@ -1391,11 +1391,16 @@ void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() { // --------------------------------------------------------------------------- sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName, - bool isSecure, const std::string& uniqueId, + bool isSecure, bool optimizeForPower, + const std::string& uniqueId, float requestedRefreshRate) { + const gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy = optimizeForPower + ? gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower + : gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance; sp<IBinder> display = nullptr; binder::Status status = ComposerServiceAIDL::getComposerService()->createVirtualDisplay(displayName, isSecure, + optimizationPolicy, uniqueId, requestedRefreshRate, &display); @@ -1523,11 +1528,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelat mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eRelativeLayerChanged; - s->what &= ~layer_state_t::eLayerChanged; - s->relativeLayerSurfaceControl = relativeTo; - s->z = z; - + s->updateRelativeLayer(relativeTo, z); registerSurfaceControlForCallback(sc); return *this; } @@ -1557,9 +1558,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrans mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eTransparentRegionChanged; - s->transparentRegion = transparentRegion; - + s->updateTransparentRegion(transparentRegion); registerSurfaceControlForCallback(sc); return *this; } @@ -1721,9 +1720,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent if (SurfaceControl::isSameSurface(sc, newParent)) { return *this; } - s->what |= layer_state_t::eReparent; - s->parentSurfaceControlForChild = newParent ? newParent->getParentingLayer() : nullptr; - + s->updateParentLayer(newParent); registerSurfaceControlForCallback(sc); return *this; } @@ -1953,9 +1950,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesir } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts( - const sp<SurfaceControl>& sc, const base::unique_fd& lutFd, - const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions, - const std::vector<int32_t>& sizes, const std::vector<int32_t>& samplingKeys) { + const sp<SurfaceControl>& sc, base::unique_fd&& lutFd, const std::vector<int32_t>& offsets, + const std::vector<int32_t>& dimensions, const std::vector<int32_t>& sizes, + const std::vector<int32_t>& samplingKeys) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; @@ -1964,8 +1961,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts( s->what |= layer_state_t::eLutsChanged; if (lutFd.ok()) { - s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets, - dimensions, sizes, samplingKeys); + s->luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), offsets, dimensions, sizes, + samplingKeys); } else { s->luts = nullptr; } @@ -2009,9 +2006,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSurfa mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eSurfaceDamageRegionChanged; - s->surfaceDamageRegion = surfaceDamageRegion; - + s->updateSurfaceDamageRegion(surfaceDamageRegion); registerSurfaceControlForCallback(sc); return *this; } @@ -2130,21 +2125,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInput mStatus = BAD_INDEX; return *this; } - s->windowInfoHandle = std::move(info); - s->what |= layer_state_t::eInputInfoChanged; + s->updateInputWindowInfo(std::move(info)); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow( const FocusRequest& request) { - mInputWindowCommands.focusRequests.push_back(request); + mInputWindowCommands.addFocusRequest(request); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addWindowInfosReportedListener( sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) { - mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener); + mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener); return *this; } @@ -2364,10 +2358,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStret return *this; } -bool SurfaceComposerClient::flagEdgeExtensionEffectUseShader() { - return com::android::graphics::libgui::flags::edge_extension_shader(); -} - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setEdgeExtensionEffect( const sp<SurfaceControl>& sc, const gui::EdgeExtensionParameters& effect) { layer_state_t* s = getLayerState(sc); @@ -2572,8 +2562,9 @@ SurfaceComposerClient::Transaction::setTrustedPresentationCallback( } s->what |= layer_state_t::eTrustedPresentationInfoChanged; s->trustedPresentationThresholds = thresholds; - s->trustedPresentationListener.callbackInterface = TransactionCompletedListener::getIInstance(); - s->trustedPresentationListener.callbackId = sc->getLayerId(); + s->trustedPresentationListener.configure( + {.callbackInterface = TransactionCompletedListener::getIInstance(), + .callbackId = sc->getLayerId()}); return *this; } @@ -2589,8 +2580,7 @@ SurfaceComposerClient::Transaction::clearTrustedPresentationCallback(const sp<Su } s->what |= layer_state_t::eTrustedPresentationInfoChanged; s->trustedPresentationThresholds = TrustedPresentationThresholds(); - s->trustedPresentationListener.callbackInterface = nullptr; - s->trustedPresentationListener.callbackId = -1; + s->trustedPresentationListener.clear(); return *this; } @@ -2683,9 +2673,9 @@ status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32 } ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err)); if (err == NO_ERROR) { - *outSurface = new SurfaceControl(this, result.handle, result.layerId, - toString(result.layerName), w, h, format, - result.transformHint, flags); + *outSurface = sp<SurfaceControl>::make(this, result.handle, result.layerId, + toString(result.layerName), w, h, format, + result.transformHint, flags); } } return err; @@ -2701,7 +2691,8 @@ sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFr const binder::Status status = mClient->mirrorSurface(mirrorFromHandle, &result); const status_t err = statusTFromBinderStatus(status); if (err == NO_ERROR) { - return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName)); + return sp<SurfaceControl>::make(this, result.handle, result.layerId, + toString(result.layerName)); } return nullptr; } @@ -2711,7 +2702,8 @@ sp<SurfaceControl> SurfaceComposerClient::mirrorDisplay(DisplayId displayId) { const binder::Status status = mClient->mirrorDisplay(displayId.value, &result); const status_t err = statusTFromBinderStatus(status); if (err == NO_ERROR) { - return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName)); + return sp<SurfaceControl>::make(this, result.handle, result.layerId, + toString(result.layerName)); } return nullptr; } @@ -2774,6 +2766,7 @@ status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId, if (status.isOk()) { // convert gui::StaticDisplayInfo to ui::StaticDisplayInfo outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType); + outInfo->port = ginfo.port; outInfo->density = ginfo.density; outInfo->secure = ginfo.secure; outInfo->installOrientation = static_cast<ui::Rotation>(ginfo.installOrientation); diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index b735418d4b..50877f8c56 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -194,7 +194,7 @@ const std::string& SurfaceControl::getName() const { return mName; } -std::shared_ptr<Choreographer> SurfaceControl::getChoreographer() { +sp<Choreographer> SurfaceControl::getChoreographer() { if (mChoreographer) { return mChoreographer; } @@ -203,7 +203,7 @@ std::shared_ptr<Choreographer> SurfaceControl::getChoreographer() { ALOGE("%s: No looper prepared for thread", __func__); return nullptr; } - mChoreographer = std::make_shared<Choreographer>(looper, getHandle()); + mChoreographer = sp<Choreographer>::make(looper, getHandle()); status_t result = mChoreographer->initialize(); if (result != OK) { ALOGE("Failed to initialize choreographer"); @@ -269,10 +269,11 @@ status_t SurfaceControl::readFromParcel(const Parcel& parcel, SAFE_PARCEL(parcel.readUint32, &format); // We aren't the original owner of the surface. - *outSurfaceControl = new SurfaceControl(new SurfaceComposerClient( - interface_cast<ISurfaceComposerClient>(client)), - handle.get(), layerId, layerName, width, height, format, - transformHint); + *outSurfaceControl = + sp<SurfaceControl>::make(sp<SurfaceComposerClient>::make( + interface_cast<ISurfaceComposerClient>(client)), + handle, layerId, layerName, width, height, format, + transformHint); return NO_ERROR; } diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING index a590c86ceb..14d6df62cb 100644 --- a/libs/gui/TEST_MAPPING +++ b/libs/gui/TEST_MAPPING @@ -60,5 +60,34 @@ } ] } + ], + "postsubmit": [ + { + "name": "libgui_test", + "keywords": [ "primary-device" ], + "options": [ + // TODO(b/397776630): Failing on real devices. + { + "exclude-filter": "InputSurfacesTest#input_respects_scaled_touchable_region_overflow" + }, + // TODO(b/233363648): Failing on real devices. + { + "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferNpot" + }, + { + "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferPow2" + }, + { + "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferWithCrop" + }, + // TODO(b/233363648): Flaky on real devices. + { + "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentBeforeConsumerDeathUnrefsBuffers" + }, + { + "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentAfterConsumerDeathUnrefsBuffers" + } + ] + } ] } diff --git a/libs/gui/TransactionState.cpp b/libs/gui/TransactionState.cpp new file mode 100644 index 0000000000..9e09bc2644 --- /dev/null +++ b/libs/gui/TransactionState.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2025 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 "TransactionState" +#include <gui/LayerState.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/TransactionState.h> +#include <private/gui/ParcelUtils.h> +#include <algorithm> + +namespace android { + +status_t TransactionState::writeToParcel(Parcel* parcel) const { + SAFE_PARCEL(parcel->writeUint64, mId); + SAFE_PARCEL(parcel->writeUint32, mFlags); + SAFE_PARCEL(parcel->writeInt64, mDesiredPresentTime); + SAFE_PARCEL(parcel->writeBool, mIsAutoTimestamp); + SAFE_PARCEL(parcel->writeParcelable, mFrameTimelineInfo); + SAFE_PARCEL(parcel->writeStrongBinder, mApplyToken); + SAFE_PARCEL(parcel->writeBool, mMayContainBuffer); + SAFE_PARCEL(parcel->writeBool, mLogCallPoints); + + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mDisplayStates.size())); + for (auto const& displayState : mDisplayStates) { + displayState.write(*parcel); + } + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mComposerStates.size())); + for (auto const& composerState : mComposerStates) { + composerState.write(*parcel); + } + + mInputWindowCommands.write(*parcel); + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size())); + for (const client_cache_t& uncacheBuffer : mUncacheBuffers) { + SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote()); + SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id); + } + + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size())); + for (auto mergedTransactionId : mMergedTransactionIds) { + SAFE_PARCEL(parcel->writeUint64, mergedTransactionId); + } + + SAFE_PARCEL(parcel->writeBool, mHasListenerCallbacks); + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mListenerCallbacks.size())); + for (const auto& [listener, callbackIds] : mListenerCallbacks) { + SAFE_PARCEL(parcel->writeStrongBinder, listener); + SAFE_PARCEL(parcel->writeParcelableVector, callbackIds); + } + + return NO_ERROR; +} + +status_t TransactionState::readFromParcel(const Parcel* parcel) { + SAFE_PARCEL(parcel->readUint64, &mId); + SAFE_PARCEL(parcel->readUint32, &mFlags); + SAFE_PARCEL(parcel->readInt64, &mDesiredPresentTime); + SAFE_PARCEL(parcel->readBool, &mIsAutoTimestamp); + SAFE_PARCEL(parcel->readParcelable, &mFrameTimelineInfo); + SAFE_PARCEL(parcel->readNullableStrongBinder, &mApplyToken); + SAFE_PARCEL(parcel->readBool, &mMayContainBuffer); + SAFE_PARCEL(parcel->readBool, &mLogCallPoints); + + uint32_t count; + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()) + mDisplayStates.clear(); + mDisplayStates.reserve(count); + for (size_t i = 0; i < count; i++) { + DisplayState displayState; + if (displayState.read(*parcel) == BAD_VALUE) { + return BAD_VALUE; + } + mDisplayStates.emplace_back(std::move(displayState)); + } + + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()) + mComposerStates.clear(); + mComposerStates.reserve(count); + for (size_t i = 0; i < count; i++) { + ComposerState composerState; + if (composerState.read(*parcel) == BAD_VALUE) { + return BAD_VALUE; + } + mComposerStates.emplace_back(std::move(composerState)); + } + + if (status_t status = mInputWindowCommands.read(*parcel) != NO_ERROR) { + return status; + } + + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()) + mUncacheBuffers.clear(); + mUncacheBuffers.reserve(count); + for (size_t i = 0; i < count; i++) { + client_cache_t client_cache; + sp<IBinder> tmpBinder; + SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder); + client_cache.token = tmpBinder; + SAFE_PARCEL(parcel->readUint64, &client_cache.id); + mUncacheBuffers.emplace_back(std::move(client_cache)); + } + + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()) + mMergedTransactionIds.clear(); + mMergedTransactionIds.resize(count); + for (size_t i = 0; i < count; i++) { + SAFE_PARCEL(parcel->readUint64, &mMergedTransactionIds[i]); + } + + SAFE_PARCEL(parcel->readBool, &mHasListenerCallbacks); + SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize()); + mListenerCallbacks.clear(); + mListenerCallbacks.reserve(count); + for (uint32_t i = 0; i < count; i++) { + sp<IBinder> tmpBinder; + SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder); + std::vector<CallbackId> callbackIds; + SAFE_PARCEL(parcel->readParcelableVector, &callbackIds); + mListenerCallbacks.emplace_back(tmpBinder, callbackIds); + } + + return NO_ERROR; +} + +void TransactionState::merge(TransactionState&& other, + const std::function<void(layer_state_t&)>& onBufferOverwrite) { + while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() > + MAX_MERGE_HISTORY_LENGTH - 1 && + mMergedTransactionIds.size() > 0) { + mMergedTransactionIds.pop_back(); + } + if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) { + mMergedTransactionIds.insert(mMergedTransactionIds.begin(), + other.mMergedTransactionIds.begin(), + other.mMergedTransactionIds.end() - 1); + } else if (other.mMergedTransactionIds.size() > 0u) { + mMergedTransactionIds.insert(mMergedTransactionIds.begin(), + other.mMergedTransactionIds.begin(), + other.mMergedTransactionIds.end()); + } + mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId); + + for (auto const& otherState : other.mComposerStates) { + if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(), + [&otherState](const auto& composerState) { + return composerState.state.surface == + otherState.state.surface; + }); + it != mComposerStates.end()) { + if (otherState.state.what & layer_state_t::eBufferChanged) { + onBufferOverwrite(it->state); + } + it->state.merge(otherState.state); + } else { + mComposerStates.push_back(otherState); + } + } + + for (auto const& state : other.mDisplayStates) { + if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(), + [&state](const auto& displayState) { + return displayState.token == state.token; + }); + it != mDisplayStates.end()) { + it->merge(state); + } else { + mDisplayStates.push_back(state); + } + } + + for (const auto& cacheId : other.mUncacheBuffers) { + mUncacheBuffers.push_back(cacheId); + } + + mInputWindowCommands.merge(other.mInputWindowCommands); + // TODO(b/385156191) Consider merging desired present time. + mFlags |= other.mFlags; + mMayContainBuffer |= other.mMayContainBuffer; + mLogCallPoints |= other.mLogCallPoints; + + // mApplyToken is explicitly not merged. Token should be set before applying the transactions to + // make synchronization decisions a bit simpler. + mergeFrameTimelineInfo(other.mFrameTimelineInfo); + other.clear(); +} + +// copied from FrameTimelineInfo::merge() +void TransactionState::mergeFrameTimelineInfo(const FrameTimelineInfo& other) { + // When merging vsync Ids we take the oldest valid one + if (mFrameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID && + other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { + if (other.vsyncId > mFrameTimelineInfo.vsyncId) { + mFrameTimelineInfo = other; + } + } else if (mFrameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { + mFrameTimelineInfo = other; + } +} + +void TransactionState::clear() { + mComposerStates.clear(); + mDisplayStates.clear(); + mListenerCallbacks.clear(); + mHasListenerCallbacks = false; + mInputWindowCommands.clear(); + mUncacheBuffers.clear(); + mDesiredPresentTime = 0; + mIsAutoTimestamp = true; + mApplyToken = nullptr; + mFrameTimelineInfo = {}; + mMergedTransactionIds.clear(); + mFlags = 0; + mMayContainBuffer = false; + mLogCallPoints = false; +} + +layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) { + auto handle = sc->getLayerStateHandle(); + if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(), + [&handle](const auto& composerState) { + return composerState.state.surface == handle; + }); + it != mComposerStates.end()) { + return &it->state; + } + + // we don't have it, add an initialized layer_state to our list + ComposerState s; + s.state.surface = handle; + s.state.layerId = sc->getLayerId(); + mComposerStates.push_back(s); + + return &mComposerStates.back().state; +} + +DisplayState& TransactionState::getDisplayState(const sp<IBinder>& token) { + if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(), + [token](const auto& display) { return display.token == token; }); + it != mDisplayStates.end()) { + return *it; + } + + // If display state doesn't exist, add a new one. + DisplayState s; + s.token = token; + mDisplayStates.push_back(s); + return mDisplayStates.back(); +} + +}; // namespace android diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp index 91c9a85149..d633f9f15e 100644 --- a/libs/gui/WindowInfosListenerReporter.cpp +++ b/libs/gui/WindowInfosListenerReporter.cpp @@ -15,6 +15,7 @@ */ #include <android/gui/ISurfaceComposer.h> +#include <android/gui/IWindowInfosListener.h> #include <gui/AidlUtil.h> #include <gui/WindowInfosListenerReporter.h> #include "gui/WindowInfosUpdate.h" @@ -27,7 +28,7 @@ using gui::WindowInfosListener; using gui::aidl_utils::statusTFromBinderStatus; sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() { - static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter; + static sp<WindowInfosListenerReporter> sInstance = sp<WindowInfosListenerReporter>::make(); return sInstance; } @@ -116,7 +117,8 @@ void WindowInfosListenerReporter::reconnect(const sp<gui::ISurfaceComposer>& com std::scoped_lock lock(mListenersMutex); if (!mWindowInfosListeners.empty()) { gui::WindowInfosListenerInfo listenerInfo; - composerService->addWindowInfosListener(this, &listenerInfo); + composerService->addWindowInfosListener(sp<gui::IWindowInfosListener>::fromExisting(this), + &listenerInfo); mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher); mListenerId = listenerInfo.listenerId; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index da47ee27ba..9b2f089665 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -67,6 +67,11 @@ interface ISurfaceComposer { frameRateOverride = 1 << 1, } + enum OptimizationPolicy { + optimizeForPower = 0, + optimizeForPerformance = 1, + } + /** * Signal that we're done booting. * Requires ACCESS_SURFACE_FLINGER permission @@ -97,6 +102,10 @@ interface ISurfaceComposer { * The name of the virtual display. * isSecure * Whether this virtual display is secure. + * optimizationPolicy + * Whether to optimize for power or performance. Displays that are optimizing for power may + * be dependent on a different display that optimizes for performance when they are on, + * which will guarantee performance for all of the other displays. * uniqueId * The unique ID for the display. * requestedRefreshRate @@ -108,7 +117,7 @@ interface ISurfaceComposer { * requires ACCESS_SURFACE_FLINGER permission. */ @nullable IBinder createVirtualDisplay(@utf8InCpp String displayName, boolean isSecure, - @utf8InCpp String uniqueId, float requestedRefreshRate); + OptimizationPolicy optimizationPolicy, @utf8InCpp String uniqueId, float requestedRefreshRate); /** * Destroy a virtual display. diff --git a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl index 0ccda56ef5..7ff332c29e 100644 --- a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl +++ b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl @@ -23,6 +23,7 @@ import android.gui.Rotation; /** @hide */ parcelable StaticDisplayInfo { DisplayConnectionType connectionType = DisplayConnectionType.Internal; + int port = -1; float density; boolean secure; @nullable DeviceProductInfo deviceProductInfo; diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp index fd8ffe1f01..b1a23b309e 100644 --- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp @@ -971,7 +971,7 @@ inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode( // H2BGraphicBufferProducer status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { - *buf = new GraphicBuffer(); + *buf = sp<GraphicBuffer>::make(); status_t fnStatus; status_t transStatus = toStatusT(mBase->requestBuffer( static_cast<int32_t>(slot), @@ -999,7 +999,7 @@ status_t H2BGraphicBufferProducer::dequeueBuffer(int* slot, sp<Fence>* fence, ui uint32_t h, ::android::PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { - *fence = new Fence(); + *fence = sp<Fence>::make(); status_t fnStatus; status_t transStatus = toStatusT(mBase->dequeueBuffer( w, h, static_cast<PixelFormat>(format), uint32_t(usage), @@ -1035,8 +1035,8 @@ status_t H2BGraphicBufferProducer::detachBuffer(int slot) { status_t H2BGraphicBufferProducer::detachNextBuffer( sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { - *outBuffer = new GraphicBuffer(); - *outFence = new Fence(); + *outBuffer = sp<GraphicBuffer>::make(); + *outFence = sp<Fence>::make(); status_t fnStatus; status_t transStatus = toStatusT(mBase->detachNextBuffer( [&fnStatus, outBuffer, outFence] ( @@ -1127,8 +1127,8 @@ int H2BGraphicBufferProducer::query(int what, int* value) { status_t H2BGraphicBufferProducer::connect( const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) { - sp<HProducerListener> tListener = listener == nullptr ? - nullptr : new B2HProducerListener(listener); + sp<HProducerListener> tListener = + listener == nullptr ? nullptr : sp<B2HProducerListener>::make(listener); status_t fnStatus; status_t transStatus = toStatusT(mBase->connect( tListener, static_cast<int32_t>(api), producerControlledByApp, @@ -1205,13 +1205,13 @@ status_t H2BGraphicBufferProducer::getLastQueuedBuffer( hidl_handle const& fence, hidl_array<float, 16> const& transformMatrix) { fnStatus = toStatusT(status); - *outBuffer = new GraphicBuffer(); + *outBuffer = sp<GraphicBuffer>::make(); if (!convertTo(outBuffer->get(), buffer)) { ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - " "Invalid output buffer"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } - *outFence = new Fence(); + *outFence = sp<Fence>::make(); if (!convertTo(outFence->get(), fence)) { ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - " "Invalid output fence"); diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp index c76d771262..4384bd5faa 100644 --- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp @@ -272,7 +272,7 @@ Return<void> B2HGraphicBufferProducer::connect( HConnectionType hConnectionType, bool producerControlledByApp, connect_cb _hidl_cb) { - sp<BProducerListener> bListener = new H2BProducerListener(hListener); + sp<BProducerListener> bListener = sp<H2BProducerListener>::make(hListener); int bConnectionType{}; if (!bListener || !h2b(hConnectionType, &bConnectionType)) { _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{}); diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp index ae00a2642e..7121bb7aef 100644 --- a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp @@ -325,7 +325,7 @@ status_t H2BGraphicBufferProducer::connect( } sp<HProducerListener> hListener = nullptr; if (listener && listener->needsReleaseNotify()) { - hListener = new B2HProducerListener(listener); + hListener = sp<B2HProducerListener>::make(listener); if (!hListener) { LOG(ERROR) << "connect: failed to wrap listener."; return UNKNOWN_ERROR; diff --git a/libs/gui/bufferqueue/2.0/types.cpp b/libs/gui/bufferqueue/2.0/types.cpp index cbd6cad847..c245766b42 100644 --- a/libs/gui/bufferqueue/2.0/types.cpp +++ b/libs/gui/bufferqueue/2.0/types.cpp @@ -147,13 +147,13 @@ bool b2h(sp<BFence> const& from, HFenceWrapper* to) { bool h2b(native_handle_t const* from, sp<BFence>* to) { if (!from || from->numFds == 0) { - *to = new ::android::Fence(); + *to = sp<::android::Fence>::make(); return true; } if (from->numFds != 1 || from->numInts != 0) { return false; } - *to = new BFence(dup(from->data[0])); + *to = sp<BFence>::make(dup(from->data[0])); return true; } diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index cdc2150a26..db1b9fb8eb 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -46,21 +46,6 @@ class BufferItemConsumer; class BLASTBufferItemConsumer : public BufferItemConsumer { public: -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer, - const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, - int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) - : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp), -#else - BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, - int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) - : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp), -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - mBLASTBufferQueue(std::move(bbq)), - mCurrentlyConnected(false), - mPreviouslyConnected(false) { - } - void onDisconnect() override EXCLUDES(mMutex); void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex); @@ -81,6 +66,23 @@ protected: #endif private: +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer, + const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, + int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) + : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp), +#else + BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, + int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq) + : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp), +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + mBLASTBufferQueue(std::move(bbq)), + mCurrentlyConnected(false), + mPreviouslyConnected(false) { + } + + friend class sp<BLASTBufferItemConsumer>; + const wp<BLASTBufferQueue> mBLASTBufferQueue; uint64_t mCurrentFrameNumber GUARDED_BY(mMutex) = 0; @@ -94,8 +96,6 @@ private: class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener { public: - BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true); - sp<IGraphicBufferProducer> getIGraphicBufferProducer() const { return mProducer; } @@ -158,8 +158,13 @@ public: void onFirstRef() override; private: + // Not public to ensure construction via sp<>::make(). + BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true); + + friend class sp<BLASTBufferQueue>; friend class BLASTBufferQueueHelper; friend class BBQBufferQueueProducer; + friend class TestBLASTBufferQueue; #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) friend class BBQBufferQueueCore; #endif diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h index 6810edaf7c..fc31f4636c 100644 --- a/libs/gui/include/gui/BufferItemConsumer.h +++ b/libs/gui/include/gui/BufferItemConsumer.h @@ -47,6 +47,16 @@ class BufferItemConsumer: public ConsumerBase enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT }; enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; + static std::tuple<sp<BufferItemConsumer>, sp<Surface>> create( + uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS, + bool controlledByApp = false, bool isConsumerSurfaceFlinger = false); + + static sp<BufferItemConsumer> create(const sp<IGraphicBufferConsumer>& consumer, + uint64_t consumerUsage, + int bufferCount = DEFAULT_MAX_BUFFERS, + bool controlledByApp = false) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); + // Create a new buffer item consumer. The consumerUsage parameter determines // the consumer usage flags passed to the graphics allocator. The // bufferCount parameter specifies how many buffers can be locked for user @@ -86,6 +96,14 @@ class BufferItemConsumer: public ConsumerBase status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen, bool waitForFence = true); + // Transfer ownership of a buffer to the BufferQueue. On NO_ERROR, the buffer + // is considered as if it were acquired. Buffer must not be null. + // + // Returns + // - BAD_VALUE if buffer is null + // - INVALID_OPERATION if too many buffers have already been acquired + status_t attachBuffer(const sp<GraphicBuffer>& buffer); + // Returns an acquired buffer to the queue, allowing it to be reused. Since // only a fixed number of buffers may be acquired at a time, old buffers // must be released by calling releaseBuffer to ensure new buffers can be @@ -95,10 +113,8 @@ class BufferItemConsumer: public ConsumerBase status_t releaseBuffer(const BufferItem &item, const sp<Fence>& releaseFence = Fence::NO_FENCE); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) status_t releaseBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence = Fence::NO_FENCE); -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) protected: #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h index ab1231ad6b..ba6a6a73f4 100644 --- a/libs/gui/include/gui/BufferQueueConsumer.h +++ b/libs/gui/include/gui/BufferQueueConsumer.h @@ -122,7 +122,10 @@ public: // setMaxAcquiredBufferCount sets the maximum number of buffers that can // be acquired by the consumer at one time (default 1). This call will // fail if a producer is connected to the BufferQueue. - virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override; + virtual status_t setMaxAcquiredBufferCount( + int maxAcquiredBuffers, + std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) override; // setConsumerName sets the name used in logging status_t setConsumerName(const String8& name) override; diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index a93ba14c57..5862967d4a 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -103,7 +103,7 @@ public: virtual void handleMessage(const Message& message) override; static void initJVM(JNIEnv* env); - static Choreographer* getForThread(); + static sp<Choreographer> getForThread(); static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock); static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock); virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h index 2e347c94f5..d2215ef7e6 100644 --- a/libs/gui/include/gui/ConsumerBase.h +++ b/libs/gui/include/gui/ConsumerBase.h @@ -123,9 +123,7 @@ public: // See IGraphicBufferConsumer::setMaxAcquiredBufferCount status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) status_t setConsumerIsProtected(bool isProtected); -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // See IGraphicBufferConsumer::getSidebandStream sp<NativeHandle> getSidebandStream() const; @@ -141,7 +139,8 @@ private: ConsumerBase(const ConsumerBase&); void operator=(const ConsumerBase&); - void initialize(bool controlledByApp); + // Requires `this` to be sp/wp so must not be called from ctor. + void initialize(); protected: // ConsumerBase constructs a new ConsumerBase object to consume image @@ -192,6 +191,8 @@ protected: #endif virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer); + virtual void onBuffersReleasedLocked(); + virtual status_t detachBufferLocked(int slotIndex); // freeBufferLocked frees up the given buffer slot. If the slot has been @@ -254,6 +255,10 @@ protected: const sp<GraphicBuffer> graphicBuffer, EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR); #endif + // Required to complete initialization, so `final` lest overrides forget to + // delegate. + void onFirstRef() override final; + // returns true iff the slot still has the graphicBuffer in it. bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer); @@ -329,6 +334,8 @@ protected: // releaseBufferLocked. sp<Fence> mPrevFinalReleaseFence; + const bool mIsControlledByApp; + // mMutex is the mutex used to prevent concurrent access to the member // variables of ConsumerBase objects. It must be locked whenever the // member variables are accessed or when any of the *Locked methods are diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h index 2bba61bbe8..995cdfb53d 100644 --- a/libs/gui/include/gui/CpuConsumer.h +++ b/libs/gui/include/gui/CpuConsumer.h @@ -31,6 +31,7 @@ namespace android { class BufferQueue; class GraphicBuffer; class String8; +class Surface; /** * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU @@ -92,6 +93,13 @@ class CpuConsumer : public ConsumerBase // Create a new CPU consumer. The maxLockedBuffers parameter specifies // how many buffers can be locked for user access at the same time. + static std::tuple<sp<CpuConsumer>, sp<Surface>> create(size_t maxLockedBuffers, + bool controlledByApp = false, + bool isConsumerSurfaceFlinger = false); + static sp<CpuConsumer> create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, + bool controlledByApp = false) + __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false, bool isConsumerSurfaceFlinger = false); @@ -100,8 +108,8 @@ class CpuConsumer : public ConsumerBase bool controlledByApp = false) __attribute((deprecated("Prefer ctors that create their own surface and consumer."))); #else - CpuConsumer(const sp<IGraphicBufferConsumer>& bq, - size_t maxLockedBuffers, bool controlledByApp = false); + CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers, + bool controlledByApp = false); #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) // Gets the next graphics buffer from the producer and locks it for CPU use, diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h index ab86ac4af8..187381c4ae 100644 --- a/libs/gui/include/gui/DisplayLuts.h +++ b/libs/gui/include/gui/DisplayLuts.h @@ -18,6 +18,10 @@ #include <android-base/unique_fd.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> +#include <cutils/ashmem.h> +#include <sys/mman.h> +#include <algorithm> +#include <ostream> #include <vector> namespace android::gui { @@ -62,4 +66,99 @@ private: base::unique_fd fd; }; // struct DisplayLuts +static inline void PrintTo(const std::vector<int32_t>& offsets, ::std::ostream* os) { + *os << "\n .offsets = {"; + for (size_t i = 0; i < offsets.size(); i++) { + *os << offsets[i]; + if (i != offsets.size() - 1) { + *os << ", "; + } + } + *os << "}"; +} + +static inline void PrintTo(const std::vector<DisplayLuts::Entry>& entries, ::std::ostream* os) { + *os << "\n .lutProperties = {\n"; + for (auto& [dimension, size, samplingKey] : entries) { + *os << " Entry{" + << "dimension: " << dimension << ", size: " << size << ", samplingKey: " << samplingKey + << "}\n"; + } + *os << " }"; +} + +static constexpr size_t kMaxPrintCount = 100; + +static inline void PrintTo(const std::vector<float>& buffer, size_t offset, int32_t dimension, + size_t size, ::std::ostream* os) { + size_t range = std::min(size, kMaxPrintCount); + *os << "{"; + if (dimension == 1) { + for (size_t i = 0; i < range; i++) { + *os << buffer[offset + i]; + if (i != range - 1) { + *os << ", "; + } + } + } else { + *os << "\n {R channel:"; + for (size_t i = 0; i < range; i++) { + *os << buffer[offset + i]; + if (i != range - 1) { + *os << ", "; + } + } + *os << "}\n {G channel:"; + for (size_t i = 0; i < range; i++) { + *os << buffer[offset + size + i]; + if (i != range - 1) { + *os << ", "; + } + } + *os << "}\n {B channel:"; + for (size_t i = 0; i < range; i++) { + *os << buffer[offset + 2 * size + i]; + if (i != range - 1) { + *os << ", "; + } + } + } + *os << "}"; +} + +static inline void PrintTo(const std::shared_ptr<DisplayLuts> luts, ::std::ostream* os) { + *os << "gui::DisplayLuts {"; + auto& fd = luts->getLutFileDescriptor(); + *os << "\n .pfd = " << fd.get(); + if (fd.ok()) { + PrintTo(luts->offsets, os); + PrintTo(luts->lutProperties, os); + // decode luts + int32_t fullLength = luts->offsets[luts->offsets.size() - 1]; + if (luts->lutProperties[luts->offsets.size() - 1].dimension == 1) { + fullLength += luts->lutProperties[luts->offsets.size() - 1].size; + } else { + fullLength += (luts->lutProperties[luts->offsets.size() - 1].size * + luts->lutProperties[luts->offsets.size() - 1].size * + luts->lutProperties[luts->offsets.size() - 1].size * 3); + } + size_t bufferSize = static_cast<size_t>(fullLength) * sizeof(float); + float* ptr = (float*)mmap(NULL, bufferSize, PROT_READ, MAP_SHARED, fd.get(), 0); + if (ptr == MAP_FAILED) { + *os << "\n .bufferdata cannot mmap!"; + return; + } + std::vector<float> buffers(ptr, ptr + fullLength); + munmap(ptr, bufferSize); + + *os << "\n .bufferdata = "; + for (size_t i = 0; i < luts->offsets.size(); i++) { + PrintTo(buffers, static_cast<size_t>(luts->offsets[i]), + luts->lutProperties[i].dimension, + static_cast<size_t>(luts->lutProperties[i].size), os); + } + } + *os << "\n }"; +} + } // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index dbf707f35f..254d8ac79c 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -83,6 +83,20 @@ public: // If the constructor without the tex parameter is used, the GLConsumer is // created in a detached state, and attachToContext must be called before // calls to updateTexImage. + static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t tex, uint32_t textureTarget, + bool useFenceSync, + bool isControlledByApp); + static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t textureTarget, bool useFenceSync, + bool isControlledByApp); + static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, + uint32_t textureTarget, bool useFenceSync, bool isControlledByApp) + __attribute((deprecated( + "Prefer create functions that create their own surface and consumer."))); + static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, + bool useFenceSync, bool isControlledByApp) + __attribute((deprecated( + "Prefer create functions that create their own surface and consumer."))); + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp); diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h index 8272a591da..8066b070fb 100644 --- a/libs/gui/include/gui/IGraphicBufferConsumer.h +++ b/libs/gui/include/gui/IGraphicBufferConsumer.h @@ -243,6 +243,9 @@ public: // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot // cause the maxBufferCount value to be exceeded. // + // If called with onBuffersReleasedCallback, that call back will be called in lieu of + // IConsumerListener::onBuffersReleased. + // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the BufferQueue has been abandoned // * BAD_VALUE - one of the below conditions occurred: @@ -253,6 +256,11 @@ public: // * INVALID_OPERATION - attempting to call this after a producer connected. virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; + using OnBufferReleasedCallback = std::function<void(void)>; + virtual status_t setMaxAcquiredBufferCount( + int maxAcquiredBuffers, + std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) = 0; + // setConsumerName sets the name used in logging virtual status_t setConsumerName(const String8& name) = 0; diff --git a/libs/gui/include/gui/InputTransferToken.h b/libs/gui/include/gui/InputTransferToken.h index fb4aaa73ae..b83f24562b 100644 --- a/libs/gui/include/gui/InputTransferToken.h +++ b/libs/gui/include/gui/InputTransferToken.h @@ -39,15 +39,9 @@ public: return NO_ERROR; }; + bool operator==(const InputTransferToken& other) const { return mToken == other.mToken; } + sp<IBinder> mToken; }; -static inline bool operator==(const sp<InputTransferToken>& token1, - const sp<InputTransferToken>& token2) { - if (token1.get() == token2.get()) { - return true; - } - return token1->mToken == token2->mToken; -} - } // namespace android diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index 7ee291df4c..6381db228b 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -44,6 +44,10 @@ struct LayerMetadata : public Parcelable { LayerMetadata& operator=(const LayerMetadata& other); LayerMetadata& operator=(LayerMetadata&& other); + // Note: `default` is not feasible because Parcelable does not provide ==. + bool operator==(const LayerMetadata& rhs) const { return mMap == rhs.mMap; } + bool operator!=(const LayerMetadata&) const = default; + // Merges other into this LayerMetadata. If eraseEmpty is true, any entries in // in this whose keys are paired with empty values in other will be erased. bool merge(const LayerMetadata& other, bool eraseEmpty = false); diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index c2680a42dc..369d3d136a 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -17,9 +17,9 @@ #ifndef ANDROID_SF_LAYER_STATE_H #define ANDROID_SF_LAYER_STATE_H - #include <stdint.h> #include <sys/types.h> +#include <span> #include <android/gui/DisplayCaptureArgs.h> #include <android/gui/IWindowInfosReportedListener.h> @@ -69,21 +69,39 @@ struct client_cache_t { uint64_t id; bool operator==(const client_cache_t& other) const { return id == other.id; } + bool operator!=(const client_cache_t&) const = default; bool isValid() const { return token != nullptr; } }; class TrustedPresentationListener : public Parcelable { public: - sp<ITransactionCompletedListener> callbackInterface; - int callbackId = -1; + struct State { + sp<ITransactionCompletedListener> callbackInterface; + int callbackId = -1; + bool operator==(const State&) const = default; + bool operator!=(const State&) const = default; + }; void invoke(bool presentedWithinThresholds) { - callbackInterface->onTrustedPresentationChanged(callbackId, presentedWithinThresholds); + mState.callbackInterface->onTrustedPresentationChanged(mState.callbackId, + presentedWithinThresholds); + } + void configure(State&& state) { mState = std::move(state); } + const sp<ITransactionCompletedListener>& getCallback() { return mState.callbackInterface; } + void clear() { + mState.callbackInterface = nullptr; + mState.callbackId = -1; } status_t writeToParcel(Parcel* parcel) const; status_t readFromParcel(const Parcel* parcel); + + bool operator==(const TrustedPresentationListener& rhs) const { return mState == rhs.mState; } + bool operator!=(const TrustedPresentationListener&) const = default; + +private: + State mState; }; class BufferData : public Parcelable { @@ -309,6 +327,32 @@ struct layer_state_t { bool hasValidBuffer() const; void sanitize(int32_t permissions); + void updateTransparentRegion(const Region& transparentRegion); + const Region& getTransparentRegion() const { return mNotDefCmpState.transparentRegion; } + void updateSurfaceDamageRegion(const Region& surfaceDamageRegion); + const Region& getSurfaceDamageRegion() const { return mNotDefCmpState.surfaceDamageRegion; } + // Do not update state flags. Used to set up test state. + void setSurfaceDamageRegion(Region&& surfaceDamageRegion) { + mNotDefCmpState.surfaceDamageRegion = std::move(surfaceDamageRegion); + } + void updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z); + void updateParentLayer(const sp<SurfaceControl>& newParent); + void updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info); + const gui::WindowInfo& getWindowInfo() const { + return *mNotDefCmpState.windowInfoHandle->getInfo(); + } + gui::WindowInfo* editWindowInfo() { return mNotDefCmpState.windowInfoHandle->editInfo(); } + + const sp<SurfaceControl>& getParentSurfaceControlForChild() const { + return mNotDefCmpState.parentSurfaceControlForChild; + } + const sp<SurfaceControl>& getRelativeLayerSurfaceControl() const { + return mNotDefCmpState.relativeLayerSurfaceControl; + } + + bool operator==(const layer_state_t&) const = default; + bool operator!=(const layer_state_t&) const = default; + struct matrix22_t { float dsdx{0}; float dtdx{0}; @@ -337,28 +381,20 @@ struct layer_state_t { float clientDrawnCornerRadius; uint32_t backgroundBlurRadius; - sp<SurfaceControl> relativeLayerSurfaceControl; - - sp<SurfaceControl> parentSurfaceControlForChild; - half4 color; // non POD must be last. see write/read - Region transparentRegion; uint32_t bufferTransform; bool transformToDisplayInverse; FloatRect crop; std::shared_ptr<BufferData> bufferData = nullptr; ui::Dataspace dataspace; HdrMetadata hdrMetadata; - Region surfaceDamageRegion; int32_t api; sp<NativeHandle> sidebandStream; mat4 colorTransform; std::vector<BlurRegion> blurRegions; - sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make(); - LayerMetadata metadata; // The following refer to the alpha, and dataspace, respectively of @@ -444,6 +480,18 @@ struct layer_state_t { std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel; std::shared_ptr<gui::DisplayLuts> luts; + +protected: + struct NotDefaultComparableState { + Region transparentRegion; + Region surfaceDamageRegion; + sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make(); + sp<SurfaceControl> relativeLayerSurfaceControl; + sp<SurfaceControl> parentSurfaceControlForChild; + + bool operator==(const NotDefaultComparableState& rhs) const; + bool operator!=(const NotDefaultComparableState& rhs) const = default; + } mNotDefCmpState; }; class ComposerState { @@ -451,6 +499,9 @@ public: layer_state_t state; status_t write(Parcel& output) const; status_t read(const Parcel& input); + + bool operator==(const ComposerState&) const = default; + bool operator!=(const ComposerState&) const = default; }; struct DisplayState { @@ -516,20 +567,35 @@ struct DisplayState { status_t write(Parcel& output) const; status_t read(const Parcel& input); + + bool operator==(const DisplayState&) const = default; + bool operator!=(const DisplayState&) const = default; }; struct InputWindowCommands { - std::vector<gui::FocusRequest> focusRequests; - std::unordered_set<sp<gui::IWindowInfosReportedListener>, - SpHash<gui::IWindowInfosReportedListener>> - windowInfosReportedListeners; - + using Listener = gui::IWindowInfosReportedListener; + using ListenerSet = std::unordered_set<sp<Listener>, SpHash<Listener>>; // Merges the passed in commands and returns true if there were any changes. bool merge(const InputWindowCommands& other); bool empty() const; void clear(); + void addFocusRequest(const gui::FocusRequest& request) { focusRequests.push_back(request); } + void addWindowInfosReportedListener(const sp<Listener>& listener) { + windowInfosReportedListeners.insert(listener); + } + ListenerSet&& releaseListeners() { return std::move(windowInfosReportedListeners); } + status_t write(Parcel& output) const; status_t read(const Parcel& input); + + std::span<const gui::FocusRequest> getFocusRequests() const { return focusRequests; } + const ListenerSet& getListeners() const { return windowInfosReportedListeners; } + bool operator==(const InputWindowCommands&) const = default; + bool operator!=(const InputWindowCommands&) const = default; + +private: + std::vector<gui::FocusRequest> focusRequests; + ListenerSet windowInfosReportedListeners; }; static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { diff --git a/libs/gui/include/gui/StreamSplitter.h b/libs/gui/include/gui/StreamSplitter.h index 28237b6940..8176f753c3 100644 --- a/libs/gui/include/gui/StreamSplitter.h +++ b/libs/gui/include/gui/StreamSplitter.h @@ -153,6 +153,8 @@ private: size_t mReleaseCount; }; + friend class sp<StreamSplitter>; + // Only called from createSplitter explicit StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 10225cc114..4fda8deb9c 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -345,8 +345,6 @@ public: static std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport> getDisplayDecorationSupport(const sp<IBinder>& displayToken); - static bool flagEdgeExtensionEffectUseShader(); - /** * Returns how many picture profiles are supported by the display. * @@ -396,6 +394,7 @@ public: static const std::string kEmpty; static sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure, + bool optimizeForPower = true, const std::string& uniqueId = kEmpty, float requestedRefreshRate = 0); @@ -620,7 +619,7 @@ public: Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc, float currentBufferRatio, float desiredRatio); Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio); - Transaction& setLuts(const sp<SurfaceControl>& sc, const base::unique_fd& lutFd, + Transaction& setLuts(const sp<SurfaceControl>& sc, base::unique_fd&& lutFd, const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions, const std::vector<int32_t>& sizes, diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index 344b957ba7..91a422d155 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -26,6 +26,7 @@ #include <android/gui/ISurfaceComposerClient.h> +#include <gui/Choreographer.h> #include <ui/FrameStats.h> #include <ui/PixelFormat.h> #include <ui/Region.h> @@ -36,7 +37,6 @@ namespace android { // --------------------------------------------------------------------------- -class Choreographer; class IGraphicBufferProducer; class Surface; class SurfaceComposerClient; @@ -82,7 +82,7 @@ public: const std::string& getName() const; // TODO(b/267195698): Consider renaming. - std::shared_ptr<Choreographer> getChoreographer(); + sp<Choreographer> getChoreographer(); sp<IGraphicBufferProducer> getIGraphicBufferProducer(); @@ -134,7 +134,7 @@ private: PixelFormat mFormat = PIXEL_FORMAT_NONE; uint32_t mCreateFlags = 0; uint64_t mFallbackFrameNumber = 100; - std::shared_ptr<Choreographer> mChoreographer; + sp<Choreographer> mChoreographer; }; }; // namespace android diff --git a/libs/gui/include/gui/TransactionState.h b/libs/gui/include/gui/TransactionState.h new file mode 100644 index 0000000000..4358227dae --- /dev/null +++ b/libs/gui/include/gui/TransactionState.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 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/gui/FrameTimelineInfo.h> +#include <binder/Parcelable.h> +#include <gui/LayerState.h> + +namespace android { + +// Class to store all the transaction data and the parcelling logic +class TransactionState { +public: + explicit TransactionState() = default; + TransactionState(TransactionState const& other) = default; + status_t writeToParcel(Parcel* parcel) const; + status_t readFromParcel(const Parcel* parcel); + layer_state_t* getLayerState(const sp<SurfaceControl>& sc); + DisplayState& getDisplayState(const sp<IBinder>& token); + + // Returns the current id of the transaction. + // The id is updated every time the transaction is applied. + uint64_t getId() const { return mId; } + std::vector<uint64_t> getMergedTransactionIds() const { return mMergedTransactionIds; } + void enableDebugLogCallPoints() { mLogCallPoints = true; } + void merge(TransactionState&& other, + const std::function<void(layer_state_t&)>& onBufferOverwrite); + + // copied from FrameTimelineInfo::merge() + void mergeFrameTimelineInfo(const FrameTimelineInfo& other); + void clear(); + bool operator==(const TransactionState& rhs) const = default; + bool operator!=(const TransactionState& rhs) const = default; + + uint64_t mId = 0; + std::vector<uint64_t> mMergedTransactionIds; + uint32_t mFlags = 0; + // The vsync id provided by Choreographer.getVsyncId and the input event id + gui::FrameTimelineInfo mFrameTimelineInfo; + // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction + // to be presented. When it is not possible to present at exactly that time, it will be + // presented after the time has passed. + // + // If the client didn't pass a desired presentation time, mDesiredPresentTime will be + // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true. + // + // Desired present times that are more than 1 second in the future may be ignored. + // When a desired present time has already passed, the transaction will be presented as soon + // as possible. + // + // Transactions from the same process are presented in the same order that they are applied. + // The desired present time does not affect this ordering. + int64_t mDesiredPresentTime = 0; + bool mIsAutoTimestamp = true; + // If not null, transactions will be queued up using this token otherwise a common token + // per process will be used. + sp<IBinder> mApplyToken; + // Indicates that the Transaction may contain buffers that should be cached. The reason this + // is only a guess is that buffers can be removed before cache is called. This is only a + // hint that at some point a buffer was added to this transaction before apply was called. + bool mMayContainBuffer = false; + // Prints debug logs when enabled. + bool mLogCallPoints = false; + + std::vector<DisplayState> mDisplayStates; + std::vector<ComposerState> mComposerStates; + InputWindowCommands mInputWindowCommands; + std::vector<client_cache_t> mUncacheBuffers; + // Note: mHasListenerCallbacks can be true even if mListenerCallbacks is + // empty. + bool mHasListenerCallbacks = false; + std::vector<ListenerCallbacks> mListenerCallbacks; + +private: + // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids. + // Ordered most recently merged to least recently merged. + static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u; +}; + +}; // namespace android diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 420dc2103f..9ac49c0fb6 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -268,6 +268,7 @@ struct WindowInfo : public Parcelable { bool overlaps(const WindowInfo* other) const; bool operator==(const WindowInfo& inputChannel) const; + bool operator!=(const WindowInfo&) const = default; status_t writeToParcel(android::Parcel* parcel) const override; @@ -319,6 +320,9 @@ public: status_t readFromParcel(const android::Parcel* parcel); status_t writeToParcel(android::Parcel* parcel) const; + bool operator==(const WindowInfoHandle& rhs) const { return mInfo == rhs.mInfo; } + bool operator!=(const WindowInfoHandle&) const = default; + protected: virtual ~WindowInfoHandle(); diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h index 24d26b12a1..18a7e12fa7 100644 --- a/libs/gui/include/gui/mock/GraphicBufferConsumer.h +++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h @@ -47,6 +47,7 @@ public: MOCK_METHOD2(setDefaultBufferSize, status_t(uint32_t, uint32_t)); MOCK_METHOD1(setMaxBufferCount, status_t(int)); MOCK_METHOD1(setMaxAcquiredBufferCount, status_t(int)); + MOCK_METHOD2(setMaxAcquiredBufferCount, status_t(int, std::optional<OnBufferReleasedCallback>)); MOCK_METHOD1(setConsumerName, status_t(const String8&)); MOCK_METHOD1(setDefaultBufferFormat, status_t(PixelFormat)); MOCK_METHOD1(setDefaultBufferDataSpace, status_t(android_dataspace)); diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 90d91ac06a..ce1bc9512c 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -77,14 +77,6 @@ flag { } # wb_stream_splitter flag { - name: "edge_extension_shader" - namespace: "windowing_frontend" - description: "Enable edge extension via shader" - bug: "322036393" - is_fixed_read_only: true -} # edge_extension_shader - -flag { name: "buffer_release_channel" namespace: "window_surfaces" description: "Enable BufferReleaseChannel to optimize buffer releases" @@ -138,9 +130,6 @@ flag { description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks" bug: "339705065" is_fixed_read_only: true - metadata { - purpose: PURPOSE_BUGFIX - } } # bq_gl_fence_cleanup flag { @@ -150,3 +139,25 @@ flag { bug: "340934031" is_fixed_read_only: true } # wb_media_migration + +flag { + name: "allocate_buffer_priority" + namespace: "wear_system_health" + description: "Boost priority for buffer allocation" + bug: "399701430" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} # allocate_buffer_priority + +flag { + name: "bq_always_use_max_dequeued_buffer_count" + namespace: "core_graphics" + description: "BufferQueueProducer::dequeue's respects setMaxDequeuedBufferCount even before a buffer is dequeued." + bug: "399328309" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} # bq_always_use_max_dequeued_buffer_count diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 87051a7aac..bd53031e79 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -51,11 +51,6 @@ cc_test { "-Werror", "-Wextra", "-Wthread-safety", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_UNLIMITED_SLOTS=true", ], srcs: [ @@ -90,6 +85,7 @@ cc_test { "testserver/TestServerClient.cpp", "testserver/TestServerHost.cpp", "TextureRenderer.cpp", + "TransactionState_test.cpp", "VsyncEventData_test.cpp", "WindowInfo_test.cpp", ], @@ -99,6 +95,7 @@ cc_test { "android.hardware.configstore-utils", "libSurfaceFlingerProp", "libGLESv1_CM", + "libgui", "libgui_test_server_aidl-cpp", "libinput", "libnativedisplay", diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index e6ee89f9b2..4e4c8a2b63 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -114,8 +114,8 @@ private: class BLASTBufferQueueHelper { public: BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) { - mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width, - height, PIXEL_FORMAT_RGBA_8888); + mBlastBufferQueueAdapter = sp<TestBLASTBufferQueue>::make("TestBLASTBufferQueue", sc, width, + height, PIXEL_FORMAT_RGBA_8888); } void update(const sp<SurfaceControl>& sc, int width, int height) { @@ -201,7 +201,7 @@ public: protected: void SetUp() { mComposer = ComposerService::getComposerService(); - mClient = new SurfaceComposerClient(); + mClient = sp<SurfaceComposerClient>::make(); const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); ASSERT_FALSE(ids.empty()); // display 0 is picked as this test is not much display depedent diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp index 6453885804..80eea267bf 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -24,6 +24,7 @@ #include <gui/Surface.h> #include <ui/BufferQueueDefs.h> #include <ui/GraphicBuffer.h> +#include <utils/Errors.h> #include <unordered_set> @@ -62,14 +63,15 @@ class BufferItemConsumerTest : public ::testing::Test { void SetUp() override { mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS); - mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true); + sp<Surface> surface; + std::tie(mBIC, surface) = BufferItemConsumer::create(kUsage, kMaxLockedBuffers, true); String8 name("BufferItemConsumer_Under_Test"); mBIC->setName(name); mBFL = new BufferFreedListener(this); mBIC->setBufferFreedListener(mBFL); sp<IProducerListener> producerListener = new TrackingProducerListener(this); - mProducer = mBIC->getSurface()->getIGraphicBufferProducer(); + mProducer = surface->getIGraphicBufferProducer(); IGraphicBufferProducer::QueueBufferOutput bufferOutput; ASSERT_EQ(NO_ERROR, mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU, @@ -234,6 +236,38 @@ TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) { ASSERT_EQ(1, GetFreedBufferCount()); } +TEST_F(BufferItemConsumerTest, ResizeAcquireCount) { + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 2)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 2)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1)); + EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1)); +} + +TEST_F(BufferItemConsumerTest, AttachBuffer) { + ASSERT_EQ(OK, mBIC->setMaxAcquiredBufferCount(1)); + + int slot; + DequeueBuffer(&slot); + QueueBuffer(slot); + AcquireBuffer(&slot); + + sp<GraphicBuffer> newBuffer1 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage); + sp<GraphicBuffer> newBuffer2 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage); + + // For some reason, you can attach an extra buffer? + // b/400973991 to investigate + EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer1)); + EXPECT_EQ(INVALID_OPERATION, mBIC->attachBuffer(newBuffer2)); + + ReleaseBuffer(slot); + + EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer2)); + EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer1, Fence::NO_FENCE)); + EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer2, Fence::NO_FENCE)); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) // Test that delete BufferItemConsumer triggers onBufferFreed. TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) { diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index cfbb2e7386..e22f57e43e 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -1452,10 +1452,6 @@ TEST_F(BufferQueueTest, TestProducerConnectDisconnect) { ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); } -TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) { - ASSERT_EQ(flags::bq_setframerate(), COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)); -} - struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer { BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {} @@ -1561,9 +1557,14 @@ TEST_F(BufferQueueTest, TestAdditionalOptions) { {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3}, }}; - ASSERT_EQ(NO_INIT, - native_window_set_buffers_additional_options(surface.get(), extras.data(), - extras.size())); + auto status = native_window_set_buffers_additional_options(surface.get(), extras.data(), + extras.size()); + if (flags::bq_extendedallocate()) { + ASSERT_EQ(NO_INIT, status); + } else { + ASSERT_EQ(INVALID_OPERATION, status); + GTEST_SKIP() << "Flag bq_extendedallocate not enabled"; + } if (!IsCuttlefish()) { GTEST_SKIP() << "Not cuttlefish"; diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp index 8db48d2eb0..314dea62d4 100644 --- a/libs/gui/tests/Choreographer_test.cpp +++ b/libs/gui/tests/Choreographer_test.cpp @@ -50,7 +50,7 @@ static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, v TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { sp<Looper> looper = Looper::prepare(0); - Choreographer* choreographer = Choreographer::getForThread(); + sp<Choreographer> choreographer = Choreographer::getForThread(); VsyncCallback animationCb; choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0, CALLBACK_ANIMATION); @@ -83,4 +83,4 @@ TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { animationCb.frameTime.count()); } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 9476930de3..482cfde6e9 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -66,10 +66,9 @@ protected: test_info->name(), params.width, params.height, params.maxLockedBuffers, params.format); - mCC = new CpuConsumer(params.maxLockedBuffers); + std::tie(mCC, mSTC) = CpuConsumer::create(params.maxLockedBuffers); String8 name("CpuConsumer_Under_Test"); mCC->setName(name); - mSTC = mCC->getSurface(); mANW = mSTC; } diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp index bffb3f0430..62d73ca752 100644 --- a/libs/gui/tests/DisplayedContentSampling_test.cpp +++ b/libs/gui/tests/DisplayedContentSampling_test.cpp @@ -30,7 +30,7 @@ static constexpr uint32_t INVALID_MASK = 0x10; class DisplayedContentSamplingTest : public ::testing::Test { protected: void SetUp() { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(OK, mComposerClient->initCheck()); const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); ASSERT_FALSE(ids.empty()); diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index cf05fd4ba5..5a5067b2b9 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -222,8 +222,8 @@ public: ASSERT_EQ(InputEventType::MOTION, ev->getType()); MotionEvent* mev = static_cast<MotionEvent*>(ev); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); - EXPECT_EQ(x, mev->getX(0)); - EXPECT_EQ(y, mev->getY(0)); + EXPECT_NEAR(x, mev->getX(0), EPSILON); + EXPECT_NEAR(y, mev->getY(0), EPSILON); EXPECT_EQ(flags, mev->getFlags() & flags); ev = consumeEvent(); @@ -241,8 +241,8 @@ public: MotionEvent* mev = static_cast<MotionEvent*>(ev); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); const PointerCoords& coords = *mev->getRawPointerCoords(0 /*pointerIndex*/); - EXPECT_EQ(displayX, coords.getX()); - EXPECT_EQ(displayY, coords.getY()); + EXPECT_NEAR(displayX, coords.getX(), EPSILON); + EXPECT_NEAR(displayY, coords.getY(), EPSILON); EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS); ev = consumeEvent(); @@ -398,7 +398,7 @@ public: InputSurfacesTest() { ProcessState::self()->startThreadPool(); } void SetUp() { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); ASSERT_FALSE(ids.empty()); diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp index 40af8e845a..407c18ed0d 100644 --- a/libs/gui/tests/GLTest.cpp +++ b/libs/gui/tests/GLTest.cpp @@ -56,7 +56,7 @@ void GLTest::SetUp() { } if (mDisplaySecs > 0) { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); mSurfaceControl = mComposerClient->createSurface( diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp index 2428bb3110..7ae6f40f8c 100644 --- a/libs/gui/tests/MultiTextureConsumer_test.cpp +++ b/libs/gui/tests/MultiTextureConsumer_test.cpp @@ -34,8 +34,8 @@ protected: virtual void SetUp() { GLTest::SetUp(); - mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); - mSurface = mGlConsumer->getSurface(); + std::tie(mGlConsumer, mSurface) = + GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); mANW = mSurface.get(); } diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp index c35efe28a3..d80d223f49 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -180,7 +180,7 @@ protected: } void SetUp() override { - mSurfaceComposerClient = new SurfaceComposerClient; + mSurfaceComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck()); mBackgroundLayer = diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp index 8fea689c91..a2fe8fd2ec 100644 --- a/libs/gui/tests/SamplingDemo.cpp +++ b/libs/gui/tests/SamplingDemo.cpp @@ -36,7 +36,7 @@ namespace android { class Button : public gui::BnRegionSamplingListener { public: Button(const char* name, const Rect& samplingArea) { - sp<SurfaceComposerClient> client = new SurfaceComposerClient; + sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect); diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 59d05b673c..d3434ea288 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -40,8 +40,7 @@ protected: } virtual void SetUp() { - mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false); - mSTC = mST->getSurface(); + std::tie(mST, mSTC) = GLConsumer::create(123, GLConsumer::TEXTURE_EXTERNAL, true, false); mANW = mSTC; // We need a valid GL context so we can test updateTexImage() @@ -727,8 +726,7 @@ protected: ASSERT_NE(EGL_NO_CONTEXT, mEglContext); for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { - sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false)); - sp<Surface> stc = st->getSurface(); + auto [st, stc] = GLConsumer::create(i, GLConsumer::TEXTURE_EXTERNAL, true, false); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, static_cast<ANativeWindow*>(stc.get()), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h index 1309635afd..eb31cd1be7 100644 --- a/libs/gui/tests/SurfaceTextureGL.h +++ b/libs/gui/tests/SurfaceTextureGL.h @@ -38,8 +38,7 @@ protected: void SetUp() { GLTest::SetUp(); - mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); - mSTC = mST->getSurface(); + std::tie(mST, mSTC) = GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); mANW = mSTC; ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS)); mTextureRenderer = new TextureRenderer(TEX_ID, mST); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 98d13290a0..61c93cae14 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -134,7 +134,7 @@ protected: } virtual void SetUp() { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); // TODO(brianderson): The following sometimes fails and is a source of @@ -294,9 +294,7 @@ TEST_F(SurfaceTest, LayerCountIsOne) { TEST_F(SurfaceTest, QueryConsumerUsage) { const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; - sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS); - - sp<Surface> s = c->getSurface(); + auto [c, s] = BufferItemConsumer::create(TEST_USAGE_FLAGS); sp<ANativeWindow> anw(s); int flags = -1; @@ -309,10 +307,8 @@ TEST_F(SurfaceTest, QueryConsumerUsage) { TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB; - sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); + auto [cpuConsumer, s] = CpuConsumer::create(1); cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE); - - sp<Surface> s = cpuConsumer->getSurface(); sp<ANativeWindow> anw(s); android_dataspace dataSpace; @@ -325,8 +321,7 @@ TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { } TEST_F(SurfaceTest, SettingGenerationNumber) { - sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); - sp<Surface> surface = cpuConsumer->getSurface(); + auto [cpuConsumer, surface] = CpuConsumer::create(1); sp<ANativeWindow> window(surface); // Allocate a buffer with a generation number of 0 @@ -692,10 +687,11 @@ public: return binder::Status::ok(); } - binder::Status createVirtualDisplay(const std::string& /*displayName*/, bool /*isSecure*/, - const std::string& /*uniqueId*/, - float /*requestedRefreshRate*/, - sp<IBinder>* /*outDisplay*/) override { + binder::Status createVirtualDisplay( + const std::string& /*displayName*/, bool /*isSecure*/, + gui::ISurfaceComposer::OptimizationPolicy /*optimizationPolicy*/, + const std::string& /*uniqueId*/, float /*requestedRefreshRate*/, + sp<IBinder>* /*outDisplay*/) override { return binder::Status::ok(); } @@ -2180,8 +2176,7 @@ TEST_F(SurfaceTest, BatchOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; - sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); - sp<Surface> surface = cpuConsumer->getSurface(); + auto [cpuConsumer, surface] = CpuConsumer::create(1); sp<ANativeWindow> window(surface); sp<StubSurfaceListener> listener = new StubSurfaceListener(); @@ -2229,8 +2224,7 @@ TEST_F(SurfaceTest, BatchIllegalOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; - sp<CpuConsumer> cpuConsumer = new CpuConsumer(1); - sp<Surface> surface = cpuConsumer->getSurface(); + auto [cpuConsumer, surface] = CpuConsumer::create(1); sp<ANativeWindow> window(surface); sp<StubSurfaceListener> listener = new StubSurfaceListener(); @@ -2252,6 +2246,52 @@ TEST_F(SurfaceTest, BatchIllegalOperations) { ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } +TEST_F(SurfaceTest, setMaxDequeuedBufferCount_setMaxAcquiredBufferCount_allocations) { + // + // Set up the consumer and producer--nothing fancy. + // + auto [consumer, surface] = + BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener); + sp<GraphicBuffer> buffer; + sp<Fence> fence; + + // + // These values are independent. The consumer can dequeue 3 and the consumer can acquire 3 at + // the same time. + // + ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(3)); + ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(3)); + + // + // Take all three buffers out of the queue--a fourth can't be retrieved. Then queue them. + // + std::vector<Surface::BatchBuffer> dequeuedBuffers(3); + EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers)); + if (::com::android::graphics::libgui::flags::bq_always_use_max_dequeued_buffer_count()) { + EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence)); + } + + for (auto& batchBuffer : dequeuedBuffers) { + EXPECT_EQ(OK, + surface->queueBuffer(GraphicBuffer::from(batchBuffer.buffer), + sp<Fence>::make(batchBuffer.fenceFd))); + } + dequeuedBuffers.assign(3, {}); + + // + // Acquire all three, then we should be able to dequeue 3 more. + // + std::vector<BufferItem> acquiredBuffers(3); + for (auto& bufferItem : acquiredBuffers) { + EXPECT_EQ(OK, consumer->acquireBuffer(&bufferItem, 0)); + } + + EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers)); + EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence)); +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) TEST_F(SurfaceTest, PlatformBufferMethods) { @@ -2377,8 +2417,7 @@ TEST_F(SurfaceTest, QueueAcquireReleaseDequeue_CalledInStack_DoesNotDeadlock) { sp<IGraphicBufferConsumer> bqConsumer; BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); - sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(bqConsumer, 3); - sp<Surface> surface = sp<Surface>::make(bqProducer); + auto [consumer, surface] = BufferItemConsumer::create(3); sp<ImmediateReleaseConsumerListener> consumerListener = sp<ImmediateReleaseConsumerListener>::make(consumer); consumer->setFrameAvailableListener(consumerListener); diff --git a/libs/gui/tests/TransactionState_test.cpp b/libs/gui/tests/TransactionState_test.cpp new file mode 100644 index 0000000000..179b264dbf --- /dev/null +++ b/libs/gui/tests/TransactionState_test.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2025 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 <unordered_map> +#include "android/gui/FocusRequest.h" +#include "binder/Binder.h" +#include "binder/Parcel.h" +#include "gtest/gtest.h" +#include "gui/LayerState.h" +#include "gui/WindowInfo.h" + +#include "gui/TransactionState.h" + +namespace android { + +void sprintf(std::string& out, const char* format, ...) { + va_list arg_list; + va_start(arg_list, format); + + int len = vsnprintf(nullptr, 0, format, arg_list); + if (len < 0) { + va_end(arg_list); + } + std::string line(len, '\0'); + int written = vsnprintf(line.data(), len + 1, format, arg_list); + if (written != len) { + va_end(arg_list); + } + line.pop_back(); + out += line; + va_end(arg_list); +} + +constexpr std::string dump_struct(auto& x) { + std::string s; +#if __has_builtin(__builtin_dump_struct) + __builtin_dump_struct(&x, sprintf, s); +#else + (void)x; +#endif + return s; +} + +void PrintTo(const TransactionState& state, ::std::ostream* os) { + *os << dump_struct(state); + *os << state.mFrameTimelineInfo.toString(); + for (auto mergedId : state.mMergedTransactionIds) { + *os << mergedId << ","; + } +} + +void PrintTo(const ComposerState& state, ::std::ostream* os) { + *os << dump_struct(state.state); + *os << state.state.getWindowInfo(); +} + +// In case EXPECT_EQ fails, this function is useful to pinpoint exactly which +// field did not compare ==. +void Compare(const TransactionState& s1, const TransactionState& s2) { + EXPECT_EQ(s1.mId, s2.mId); + EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds); + EXPECT_EQ(s1.mFlags, s2.mFlags); + EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo); + EXPECT_EQ(s1.mDesiredPresentTime, s2.mDesiredPresentTime); + EXPECT_EQ(s1.mIsAutoTimestamp, s2.mIsAutoTimestamp); + EXPECT_EQ(s1.mApplyToken, s2.mApplyToken); + EXPECT_EQ(s1.mMayContainBuffer, s2.mMayContainBuffer); + EXPECT_EQ(s1.mLogCallPoints, s2.mLogCallPoints); + EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size()); + EXPECT_THAT(s1.mDisplayStates, ::testing::ContainerEq(s2.mDisplayStates)); + EXPECT_EQ(s1.mComposerStates.size(), s2.mComposerStates.size()); + EXPECT_EQ(s1.mComposerStates, s2.mComposerStates); + EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands); + EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers); + EXPECT_EQ(s1.mHasListenerCallbacks, s2.mHasListenerCallbacks); + EXPECT_EQ(s1.mListenerCallbacks.size(), s2.mListenerCallbacks.size()); + EXPECT_EQ(s1.mListenerCallbacks, s2.mListenerCallbacks); +} + +std::unique_ptr<std::unordered_map<int, sp<BBinder>>> createTokenMap(size_t maxSize) { + auto result = std::make_unique<std::unordered_map<int, sp<BBinder>>>(); + for (size_t i = 0; i < maxSize; ++i) { + result->emplace(i, sp<BBinder>::make()); + } + return result; +} + +constexpr size_t kMaxComposerStates = 2; +ComposerState createComposerStateForTest(size_t i) { + static const auto* const sLayerHandle = createTokenMap(kMaxComposerStates).release(); + + ComposerState state; + state.state.what = layer_state_t::eFlagsChanged; + state.state.surface = sLayerHandle->at(i); + state.state.layerId = i; + state.state.flags = 20 * i; + return state; +} + +constexpr size_t kMaxDisplayStates = 5; +DisplayState createDisplayStateForTest(size_t i) { + static const auto* const sDisplayTokens = createTokenMap(kMaxDisplayStates).release(); + + DisplayState displayState; + displayState.what = DisplayState::eFlagsChanged; + displayState.token = sDisplayTokens->at(i); + displayState.flags = 20 * i; + return displayState; +} + +TransactionState createTransactionStateForTest() { + static sp<BBinder> sApplyToken = sp<BBinder>::make(); + + TransactionState state; + state.mId = 123; + state.mMergedTransactionIds.push_back(15); + state.mMergedTransactionIds.push_back(0); + state.mFrameTimelineInfo.vsyncId = 14; + state.mDesiredPresentTime = 11; + state.mIsAutoTimestamp = true; + state.mApplyToken = sApplyToken; + for (size_t i = 0; i < kMaxDisplayStates; i++) { + state.mDisplayStates.push_back(createDisplayStateForTest(i)); + } + for (size_t i = 0; i < kMaxComposerStates; i++) { + state.mComposerStates.push_back(createComposerStateForTest(i)); + } + static const auto* const sFocusRequestTokens = createTokenMap(5).release(); + for (int i = 0; i < 5; i++) { + gui::FocusRequest request; + request.token = sFocusRequestTokens->at(i); + request.timestamp = i; + state.mInputWindowCommands.addFocusRequest(request); + } + static const auto* const sCacheToken = createTokenMap(5).release(); + for (int i = 0; i < 5; i++) { + client_cache_t cache; + cache.token = sCacheToken->at(i); + cache.id = i; + state.mUncacheBuffers.emplace_back(std::move(cache)); + } + static const auto* const sListenerCallbacks = []() { + auto* callbacks = new std::vector<ListenerCallbacks>(); + for (int i = 0; i < 5; i++) { + callbacks->emplace_back(sp<BBinder>::make(), + std::unordered_set<CallbackId, CallbackIdHash>{}); + } + return callbacks; + }(); + state.mHasListenerCallbacks = true; + state.mListenerCallbacks = *sListenerCallbacks; + return state; +} + +TransactionState createEmptyTransaction(uint64_t id) { + TransactionState state; + state.mId = id; + return state; +} + +TEST(TransactionStateTest, parcel) { + TransactionState state = createTransactionStateForTest(); + Parcel p; + state.writeToParcel(&p); + p.setDataPosition(0); + TransactionState parcelledState; + parcelledState.readFromParcel(&p); + EXPECT_EQ(state, parcelledState); +}; + +TEST(TransactionStateTest, parcelDisplayState) { + DisplayState state = createDisplayStateForTest(0); + Parcel p; + state.write(p); + p.setDataPosition(0); + DisplayState parcelledState; + parcelledState.read(p); + EXPECT_EQ(state, parcelledState); +}; + +TEST(TransactionStateTest, parcelLayerState) { + ComposerState state = createComposerStateForTest(0); + Parcel p; + state.write(p); + p.setDataPosition(0); + ComposerState parcelledState; + parcelledState.read(p); + EXPECT_EQ(state, parcelledState); +}; + +TEST(TransactionStateTest, parcelEmptyState) { + TransactionState state; + Parcel p; + state.writeToParcel(&p); + p.setDataPosition(0); + TransactionState parcelledState; + state.readFromParcel(&p); + EXPECT_EQ(state, parcelledState); +}; + +TEST(TransactionStateTest, mergeLayerState) { + ComposerState composerState = createComposerStateForTest(0); + ComposerState update; + update.state.surface = composerState.state.surface; + update.state.layerId = 0; + update.state.what = layer_state_t::eAlphaChanged; + update.state.color.a = .42; + composerState.state.merge(update.state); + + ComposerState expectedMergedState = createComposerStateForTest(0); + expectedMergedState.state.what |= layer_state_t::eAlphaChanged; + expectedMergedState.state.color.a = .42; + EXPECT_EQ(composerState, expectedMergedState); +}; + +TEST(TransactionStateTest, merge) { + // Setup. + static constexpr uint64_t kUpdateTransactionId = 200; + + TransactionState state = createTransactionStateForTest(); + + TransactionState update; + update.mId = kUpdateTransactionId; + { + ComposerState composerState; + composerState.state.surface = state.mComposerStates[0].state.surface; + composerState.state.what = layer_state_t::eAlphaChanged; + composerState.state.color.a = .42; + update.mComposerStates.push_back(composerState); + } + { + ComposerState composerState; + composerState.state.surface = state.mComposerStates[1].state.surface; + composerState.state.what = layer_state_t::eBufferChanged; + update.mComposerStates.push_back(composerState); + } + int32_t overrwiteLayerId = -1; + // Mutation. + state.merge(std::move(update), + [&overrwiteLayerId](layer_state_t ls) { overrwiteLayerId = ls.layerId; }); + // Assertions. + EXPECT_EQ(1, overrwiteLayerId); + EXPECT_EQ(update, createEmptyTransaction(update.getId())); + + TransactionState expectedMergedState = createTransactionStateForTest(); + expectedMergedState.mMergedTransactionIds + .insert(expectedMergedState.mMergedTransactionIds.begin(), kUpdateTransactionId); + expectedMergedState.mComposerStates.at(0).state.what |= layer_state_t::eAlphaChanged; + expectedMergedState.mComposerStates.at(0).state.color.a = .42; + expectedMergedState.mComposerStates.at(1).state.what |= layer_state_t::eBufferChanged; + auto inputCommands = expectedMergedState.mInputWindowCommands; + + // desired present time is not merged. + expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime; + + EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]); + EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands); + EXPECT_EQ(state, expectedMergedState); +}; + +TEST(TransactionStateTest, clear) { + TransactionState state = createTransactionStateForTest(); + state.clear(); + TransactionState emptyState = createEmptyTransaction(state.getId()); + EXPECT_EQ(state, emptyState); +}; + +} // namespace android diff --git a/libs/input/Android.bp b/libs/input/Android.bp index d2e432063a..52e0276cad 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -225,11 +225,13 @@ cc_library { srcs: [ "AccelerationCurve.cpp", "CoordinateFilter.cpp", + "DisplayTopologyGraph.cpp", "Input.cpp", "InputConsumer.cpp", "InputConsumerNoResampling.cpp", "InputDevice.cpp", "InputEventLabels.cpp", + "InputFlags.cpp", "InputTransport.cpp", "InputVerifier.cpp", "KeyCharacterMap.cpp", @@ -269,6 +271,7 @@ cc_library { shared_libs: [ "android.companion.virtualdevice.flags-aconfig-cc", + "com.android.window.flags.window-aconfig_flags_c_lib", "libPlatformProperties", "libaconfig_storage_read_api_cc", "libbase", diff --git a/libs/input/DisplayTopologyGraph.cpp b/libs/input/DisplayTopologyGraph.cpp new file mode 100644 index 0000000000..934f2e8135 --- /dev/null +++ b/libs/input/DisplayTopologyGraph.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2025 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 "DisplayTopologyValidator" + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <ftl/enum.h> +#include <input/DisplayTopologyGraph.h> +#include <input/PrintTools.h> +#include <ui/LogicalDisplayId.h> + +#include <algorithm> + +#define INDENT " " + +namespace android { + +namespace { + +DisplayTopologyPosition getOppositePosition(DisplayTopologyPosition position) { + switch (position) { + case DisplayTopologyPosition::LEFT: + return DisplayTopologyPosition::RIGHT; + case DisplayTopologyPosition::TOP: + return DisplayTopologyPosition::BOTTOM; + case DisplayTopologyPosition::RIGHT: + return DisplayTopologyPosition::LEFT; + case DisplayTopologyPosition::BOTTOM: + return DisplayTopologyPosition::TOP; + } +} + +bool validatePrimaryDisplay(const android::DisplayTopologyGraph& displayTopologyGraph) { + return displayTopologyGraph.primaryDisplayId != ui::LogicalDisplayId::INVALID && + displayTopologyGraph.graph.contains(displayTopologyGraph.primaryDisplayId); +} + +bool validateTopologyGraph(const android::DisplayTopologyGraph& displayTopologyGraph) { + for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) { + for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : adjacentDisplays) { + const auto adjacentGraphIt = displayTopologyGraph.graph.find(adjacentDisplay.displayId); + if (adjacentGraphIt == displayTopologyGraph.graph.end()) { + LOG(ERROR) << "Missing adjacent display in topology graph: " + << adjacentDisplay.displayId << " for source " << sourceDisplay; + return false; + } + const auto reverseEdgeIt = + std::find_if(adjacentGraphIt->second.begin(), adjacentGraphIt->second.end(), + [sourceDisplay](const DisplayTopologyAdjacentDisplay& + reverseAdjacentDisplay) { + return sourceDisplay == reverseAdjacentDisplay.displayId; + }); + if (reverseEdgeIt == adjacentGraphIt->second.end()) { + LOG(ERROR) << "Missing reverse edge in topology graph for: " << sourceDisplay + << " -> " << adjacentDisplay.displayId; + return false; + } + DisplayTopologyPosition expectedPosition = + getOppositePosition(adjacentDisplay.position); + if (reverseEdgeIt->position != expectedPosition) { + LOG(ERROR) << "Unexpected reverse edge for: " << sourceDisplay << " -> " + << adjacentDisplay.displayId + << " expected position: " << ftl::enum_string(expectedPosition) + << " actual " << ftl::enum_string(reverseEdgeIt->position); + return false; + } + if (reverseEdgeIt->offsetDp != -adjacentDisplay.offsetDp) { + LOG(ERROR) << "Unexpected reverse edge offset: " << sourceDisplay << " -> " + << adjacentDisplay.displayId + << " expected offset: " << -adjacentDisplay.offsetDp << " actual " + << reverseEdgeIt->offsetDp; + return false; + } + } + } + return true; +} + +bool validateDensities(const android::DisplayTopologyGraph& displayTopologyGraph) { + for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) { + if (!displayTopologyGraph.displaysDensity.contains(sourceDisplay)) { + LOG(ERROR) << "Missing density value in topology graph for display: " << sourceDisplay; + return false; + } + } + return true; +} + +std::string logicalDisplayIdToString(const ui::LogicalDisplayId& displayId) { + return base::StringPrintf("displayId(%d)", displayId.val()); +} + +std::string adjacentDisplayToString(const DisplayTopologyAdjacentDisplay& adjacentDisplay) { + return adjacentDisplay.dump(); +} + +std::string adjacentDisplayVectorToString( + const std::vector<DisplayTopologyAdjacentDisplay>& adjacentDisplays) { + return dumpVector(adjacentDisplays, adjacentDisplayToString); +} + +} // namespace + +std::string DisplayTopologyAdjacentDisplay::dump() const { + std::string dump; + dump += base::StringPrintf("DisplayTopologyAdjacentDisplay: {displayId: %d, position: %s, " + "offsetDp: %f}", + displayId.val(), ftl::enum_string(position).c_str(), offsetDp); + return dump; +} + +bool DisplayTopologyGraph::isValid() const { + return validatePrimaryDisplay(*this) && validateTopologyGraph(*this) && + validateDensities(*this); +} + +std::string DisplayTopologyGraph::dump() const { + std::string dump; + dump += base::StringPrintf("PrimaryDisplayId: %d\n", primaryDisplayId.val()); + dump += base::StringPrintf("TopologyGraph:\n"); + dump += addLinePrefix(dumpMap(graph, logicalDisplayIdToString, adjacentDisplayVectorToString), + INDENT); + dump += "\n"; + dump += base::StringPrintf("DisplaysDensity:\n"); + dump += addLinePrefix(dumpMap(displaysDensity, logicalDisplayIdToString), INDENT); + dump += "\n"; + return dump; +} + +} // namespace android diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 65a088eb6d..155ea000e3 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -284,6 +284,36 @@ bool isStylusEvent(uint32_t source, const std::vector<PointerProperties>& proper return false; } +bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties, + int32_t action) { + return isStylusEvent(source, properties) && isHoverAction(action); +} + +bool isFromMouse(uint32_t source, ToolType toolType) { + return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::MOUSE; +} + +bool isFromTouchpad(uint32_t source, ToolType toolType) { + return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::FINGER; +} + +bool isFromDrawingTablet(uint32_t source, ToolType toolType) { + return isFromSource(source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) && + isStylusToolType(toolType); +} + +bool isHoverAction(int32_t action) { + return action == AMOTION_EVENT_ACTION_HOVER_ENTER || + action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT; +} + +bool isMouseOrTouchpad(uint32_t sources) { + // Check if this is a mouse or touchpad, but not a drawing tablet. + return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) || + (isFromSource(sources, AINPUT_SOURCE_MOUSE) && + !isFromSource(sources, AINPUT_SOURCE_STYLUS)); +} + VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), event.getSource(), event.getDisplayId()}, diff --git a/libs/input/InputFlags.cpp b/libs/input/InputFlags.cpp new file mode 100644 index 0000000000..6aa9ae6b16 --- /dev/null +++ b/libs/input/InputFlags.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2025 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/InputFlags.h> + +#include <android-base/logging.h> +#include <com_android_input_flags.h> +#include <com_android_window_flags.h> +#include <cutils/properties.h> + +#include <string> + +namespace android { + +bool InputFlags::connectedDisplaysCursorEnabled() { + if (!com::android::window::flags::enable_desktop_mode_through_dev_option()) { + return com::android::input::flags::connected_displays_cursor(); + } + static std::optional<bool> cachedDevOption; + if (!cachedDevOption.has_value()) { + char value[PROPERTY_VALUE_MAX]; + constexpr static auto sysprop_name = "persist.wm.debug.desktop_experience_devopts"; + const int devOptionEnabled = + property_get(sysprop_name, value, nullptr) > 0 ? std::atoi(value) : 0; + cachedDevOption = devOptionEnabled == 1; + } + if (cachedDevOption.value_or(false)) { + return true; + } + return com::android::input::flags::connected_displays_cursor(); +} + +bool InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled() { + return connectedDisplaysCursorEnabled() && + com::android::input::flags::connected_displays_associated_display_cursor_bugfix(); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 5bb30db772..983bbdee6e 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -26,13 +26,6 @@ flag { } flag { - name: "remove_input_channel_from_windowstate" - namespace: "input" - description: "Do not store a copy of input channel inside WindowState." - bug: "323450804" -} - -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" @@ -61,20 +54,6 @@ flag { } flag { - name: "report_palms_to_gestures_library" - namespace: "input" - description: "Report touches marked as palm by firmware to gestures library" - bug: "302505955" -} - -flag { - name: "enable_touchpad_typing_palm_rejection" - namespace: "input" - 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: "enable_v2_touchpad_typing_palm_rejection" namespace: "input" 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." @@ -134,13 +113,6 @@ flag { } flag { - name: "hide_pointer_indicators_for_secure_windows" - namespace: "input" - description: "Hide touch and pointer indicators if a secure window is present on display" - bug: "325252005" -} - -flag { name: "enable_keyboard_classifier" namespace: "input" description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic" @@ -155,13 +127,6 @@ flag { } flag { - name: "include_relative_axis_values_for_captured_touchpads" - namespace: "input" - description: "Include AXIS_RELATIVE_X and AXIS_RELATIVE_Y values when reporting touches from captured touchpads." - bug: "330522990" -} - -flag { name: "enable_per_device_input_latency_metrics" namespace: "input" description: "Capture input latency metrics on a per device granular level using histograms." @@ -239,6 +204,16 @@ flag { } flag { + name: "connected_displays_associated_display_cursor_bugfix" + namespace: "lse_desktop_experience" + description: "Apply some rules to define associated display cursor behavior in connected displays" + bug: "396568321" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "use_cloned_screen_coordinates_as_raw" namespace: "input" description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones" @@ -264,3 +239,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "enable_display_topology_validation" + namespace: "input" + description: "Set to true to enable display topology validation" + bug: "401219231" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index 7638559553..ee999f7666 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -133,6 +133,12 @@ fn process_movement( flags: u32, button_state: u32, ) -> String { + let Some(converted_source) = Source::from_bits(source) else { + panic!( + "The conversion of source 0x{source:08x} failed, please check if some sources have not \ + been added to Source." + ); + }; let Some(motion_flags) = MotionFlags::from_bits(flags) else { panic!( "The conversion of flags 0x{:08x} failed, please check if some flags have not been \ @@ -167,7 +173,7 @@ fn process_movement( } let result = verifier.process_movement(NotifyMotionArgs { device_id: DeviceId(device_id), - source: Source::from_bits(source).unwrap(), + source: converted_source, action: motion_action, pointer_properties, flags: motion_flags, diff --git a/libs/math/OWNERS b/libs/math/OWNERS index 82ae422893..08f0c5f0dc 100644 --- a/libs/math/OWNERS +++ b/libs/math/OWNERS @@ -1,5 +1,4 @@ mathias@google.com -randolphs@google.com romainguy@google.com sumir@google.com jreck@google.com diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index bed31e27a8..89a97deffd 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -142,7 +142,7 @@ static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* cho } AChoreographer* AChoreographer_getInstance() { - return Choreographer_to_AChoreographer(Choreographer::getForThread()); + return Choreographer_to_AChoreographer(Choreographer::getForThread().get()); } void AChoreographer_postFrameCallback(AChoreographer* choreographer, @@ -238,13 +238,17 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos( } AChoreographer* AChoreographer_create() { - Choreographer* choreographer = new Choreographer(nullptr); + // Increments default strongRef count on construction, will be decremented on + // function exit. + auto choreographer = sp<Choreographer>::make(nullptr); status_t result = choreographer->initialize(); if (result != OK) { ALOGW("Failed to initialize"); return nullptr; } - return Choreographer_to_AChoreographer(choreographer); + // Will be decremented and destroyed by AChoreographer_destroy + choreographer->incStrong((void*)AChoreographer_create); + return Choreographer_to_AChoreographer(choreographer.get()); } void AChoreographer_destroy(AChoreographer* choreographer) { @@ -252,7 +256,7 @@ void AChoreographer_destroy(AChoreographer* choreographer) { return; } - delete AChoreographer_to_Choreographer(choreographer); + AChoreographer_to_Choreographer(choreographer)->decStrong((void*)AChoreographer_create); } int AChoreographer_getFd(const AChoreographer* choreographer) { diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp index 937ff02241..51d0c8195a 100644 --- a/libs/nativewindow/tests/ANativeWindowTest.cpp +++ b/libs/nativewindow/tests/ANativeWindowTest.cpp @@ -50,14 +50,9 @@ protected: const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - mItemConsumer = new BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN); - mWindow = new TestableSurface(mItemConsumer->getSurface()->getIGraphicBufferProducer()); -#else - BufferQueue::createBufferQueue(&mProducer, &mConsumer); - mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN); - mWindow = new TestableSurface(mProducer); -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + sp<Surface> surface; + std::tie(mItemConsumer, surface) = BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN); + mWindow = new TestableSurface(surface->getIGraphicBufferProducer()); const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU); EXPECT_EQ(0, success); } diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 7f207f0670..f9b84fa948 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -25,6 +25,7 @@ cc_defaults { defaults: [ "android.hardware.graphics.composer3-ndk_shared", "renderengine_defaults", + "libsurfaceflinger_common_deps", ], cflags: [ "-DGL_GLEXT_PROTOTYPES", @@ -117,7 +118,10 @@ filegroup { // possible if libskia_renderengine is just pulled into librenderengine via whole_static_libs. cc_defaults { name: "librenderengine_deps", - defaults: ["skia_renderengine_deps"], + defaults: [ + "skia_renderengine_deps", + "libsurfaceflinger_common_deps", + ], static_libs: ["libskia_renderengine"], } diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp index f84db0b04c..2d18ddb0aa 100644 --- a/libs/renderengine/benchmark/Android.bp +++ b/libs/renderengine/benchmark/Android.bp @@ -28,6 +28,7 @@ cc_benchmark { "android.hardware.graphics.composer3-ndk_shared", "librenderengine_deps", "surfaceflinger_defaults", + "libsurfaceflinger_common_deps", ], srcs: [ "main.cpp", @@ -38,7 +39,6 @@ cc_benchmark { static_libs: [ "librenderengine", "libshaders", - "libsurfaceflinger_common", "libtonemap", ], cflags: [ diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index ac43da8dcf..ecb16b2e17 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -301,6 +301,10 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { *os << "\n .edgeExtensionEffect = " << settings.edgeExtensionEffect; } *os << "\n .whitePointNits = " << settings.whitePointNits; + if (settings.luts) { + *os << "\n .luts = "; + PrintTo(settings.luts, os); + } *os << "\n}"; } diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index 57041ee6a1..f43694e228 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ #include "Cache.h" + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + #include "AutoBackendTexture.h" #include "SkiaRenderEngine.h" #include "android-base/unique_fd.h" @@ -28,6 +31,7 @@ #include "utils/Timers.h" #include <com_android_graphics_libgui_flags.h> +#include <common/trace.h> namespace android::renderengine::skia { @@ -337,17 +341,17 @@ static void drawImageDimmedLayers(SkiaRenderEngine* renderengine, const DisplayS LayerSettings layer{ .geometry = Geometry{ + .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), - .boundaries = rect, - .roundedCornersCrop = rect, .roundedCornersRadius = {0.f, 0.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, - .isOpaque = true}}, + .isOpaque = true, + .maxLuminanceNits = 1000.f}}, .alpha = 1.f, .sourceDataspace = kDestDataSpace, }; @@ -370,16 +374,16 @@ static void drawTransparentImageDimmedLayers(SkiaRenderEngine* renderengine, LayerSettings layer{ .geometry = Geometry{ - .positionTransform = mat4(), .boundaries = rect, + .positionTransform = mat4(), .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = false, + .maxLuminanceNits = 1000.f, }}, .sourceDataspace = kDestDataSpace, }; @@ -421,17 +425,17 @@ static void drawClippedDimmedImageLayers(SkiaRenderEngine* renderengine, LayerSettings layer{ .geometry = Geometry{ - .positionTransform = mat4(), .boundaries = boundary, - .roundedCornersCrop = rect, + .positionTransform = mat4(), .roundedCornersRadius = {27.f, 27.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = false, + .maxLuminanceNits = 1000.f, }}, .alpha = 1.f, .sourceDataspace = kDestDataSpace, @@ -489,17 +493,17 @@ static void drawBT2020ImageLayers(SkiaRenderEngine* renderengine, const DisplayS LayerSettings layer{ .geometry = Geometry{ + .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), - .boundaries = rect, - .roundedCornersCrop = rect, .roundedCornersRadius = {0.f, 0.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, - .isOpaque = true}}, + .isOpaque = true, + .maxLuminanceNits = 1000.f}}, .alpha = 1.f, .sourceDataspace = kBT2020DataSpace, }; @@ -527,17 +531,17 @@ static void drawBT2020ClippedImageLayers(SkiaRenderEngine* renderengine, LayerSettings layer{ .geometry = Geometry{ - .positionTransform = kScaleAsymmetric, .boundaries = boundary, - .roundedCornersCrop = rect, + .positionTransform = kScaleAsymmetric, .roundedCornersRadius = {64.1f, 64.1f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = true, + .maxLuminanceNits = 1000.f, }}, .alpha = 0.5f, .sourceDataspace = kBT2020DataSpace, @@ -556,17 +560,17 @@ static void drawExtendedHDRImageLayers(SkiaRenderEngine* renderengine, LayerSettings layer{ .geometry = Geometry{ + .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), - .boundaries = rect, - .roundedCornersCrop = rect, .roundedCornersRadius = {50.f, 50.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, - .isOpaque = true}}, + .isOpaque = true, + .maxLuminanceNits = 1000.f}}, .alpha = 0.5f, .sourceDataspace = kExtendedHdrDataSpce, }; @@ -594,17 +598,17 @@ static void drawP3ImageLayers(SkiaRenderEngine* renderengine, const DisplaySetti LayerSettings layer{ .geometry = Geometry{ + .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), - .boundaries = rect, - .roundedCornersCrop = rect, .roundedCornersRadius = {50.f, 50.f}, + .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, - .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, - .isOpaque = false}}, + .isOpaque = false, + .maxLuminanceNits = 1000.f}}, .alpha = 0.5f, .sourceDataspace = kOtherDataSpace, }; @@ -659,6 +663,7 @@ static void drawEdgeExtensionLayers(SkiaRenderEngine* renderengine, const Displa // in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp // gPrintSKSL = true void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig config) { + SFTRACE_CALL(); const int previousCount = renderengine->reportShadersCompiled(); if (previousCount) { ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount); @@ -724,24 +729,29 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co impl::ExternalTexture::Usage::WRITEABLE); if (config.cacheHolePunchLayer) { + SFTRACE_NAME("cacheHolePunchLayer"); drawHolePunchLayer(renderengine, display, dstTexture); } if (config.cacheSolidLayers) { + SFTRACE_NAME("cacheSolidLayers"); drawSolidLayers(renderengine, display, dstTexture); drawSolidLayers(renderengine, p3Display, dstTexture); } if (config.cacheSolidDimmedLayers) { + SFTRACE_NAME("cacheSolidDimmedLayers"); drawSolidDimmedLayers(renderengine, display, dstTexture); } if (config.cacheShadowLayers) { + SFTRACE_NAME("cacheShadowLayers"); drawShadowLayers(renderengine, display, srcTexture); drawShadowLayers(renderengine, p3Display, srcTexture); } if (renderengine->supportsBackgroundBlur()) { + SFTRACE_NAME("supportsBackgroundBlur"); drawBlurLayers(renderengine, display, dstTexture); } @@ -776,32 +786,37 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co for (auto texture : textures) { if (config.cacheImageLayers) { + SFTRACE_NAME("cacheImageLayers"); drawImageLayers(renderengine, display, dstTexture, texture); } if (config.cacheImageDimmedLayers) { + SFTRACE_NAME("cacheImageDimmedLayers"); drawImageDimmedLayers(renderengine, display, dstTexture, texture); drawImageDimmedLayers(renderengine, p3Display, dstTexture, texture); drawImageDimmedLayers(renderengine, bt2020Display, dstTexture, texture); } if (config.cacheClippedLayers) { + SFTRACE_NAME("cacheClippedLayers"); // Draw layers for b/185569240. drawClippedLayers(renderengine, display, dstTexture, texture); } - if (com::android::graphics::libgui::flags::edge_extension_shader() && - config.cacheEdgeExtension) { + if (config.cacheEdgeExtension) { + SFTRACE_NAME("cacheEdgeExtension"); drawEdgeExtensionLayers(renderengine, display, dstTexture, texture); drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture); } } if (config.cachePIPImageLayers) { + SFTRACE_NAME("cachePIPImageLayers"); drawPIPImageLayer(renderengine, display, dstTexture, externalTexture); } if (config.cacheTransparentImageDimmedLayers) { + SFTRACE_NAME("cacheTransparentImageDimmedLayers"); drawTransparentImageDimmedLayers(renderengine, bt2020Display, dstTexture, externalTexture); drawTransparentImageDimmedLayers(renderengine, display, dstTexture, externalTexture); @@ -811,10 +826,12 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co } if (config.cacheClippedDimmedImageLayers) { + SFTRACE_NAME("cacheClippedDimmedImageLayers"); drawClippedDimmedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); } if (config.cacheUltraHDR) { + SFTRACE_NAME("cacheUltraHDR"); drawBT2020ClippedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); drawBT2020ImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); @@ -833,7 +850,10 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co }; auto layers = std::vector<LayerSettings>{layer}; // call get() to make it synchronous - renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get(); + { + SFTRACE_NAME("finalLayer"); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get(); + } const nsecs_t timeAfter = systemTime(); const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 5f2d1b1be6..9e1c226371 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -347,6 +347,7 @@ void SkiaRenderEngine::useProtectedContext(bool useProtectedContext) { if (useProtectedContextImpl( useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) { mInProtectedContext = useProtectedContext; + SFTRACE_INT("RE inProtectedContext", mInProtectedContext); // given that we are sharing the same thread between two contexts we need to // make sure that the thread state is reset when switching between the two. if (getActiveContext()) { @@ -1235,6 +1236,16 @@ void SkiaRenderEngine::drawLayersInternal( LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); trace(drawFence); + FenceTimePtr fenceTime = FenceTime::makeValid(drawFence); + for (const auto& layer : layers) { + if (FlagManager::getInstance().monitor_buffer_fences()) { + if (layer.source.buffer.buffer) { + layer.source.buffer.buffer->getBuffer() + ->getDependencyMonitor() + .addAccessCompletion(fenceTime, "RE"); + } + } + } resultPromise->set_value(std::move(drawFence)); } diff --git a/libs/renderengine/skia/debug/CaptureTimer.cpp b/libs/renderengine/skia/debug/CaptureTimer.cpp index 11bcdb8996..1c1ee0abb3 100644 --- a/libs/renderengine/skia/debug/CaptureTimer.cpp +++ b/libs/renderengine/skia/debug/CaptureTimer.cpp @@ -30,7 +30,7 @@ namespace skia { void CaptureTimer::setTimeout(TimeoutCallback function, std::chrono::milliseconds delay) { this->clear = false; - CommonPool::post([=]() { + CommonPool::post([=,this]() { if (this->clear) return; std::this_thread::sleep_for(delay); if (this->clear) return; diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp index 4164c4b4c9..f007427f1c 100644 --- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp +++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp @@ -58,9 +58,6 @@ static const SkString edgeShader = SkString(R"( )"); EdgeExtensionShaderFactory::EdgeExtensionShaderFactory() { - if (!com::android::graphics::libgui::flags::edge_extension_shader()) { - return; - } mResult = std::make_unique<SkRuntimeEffect::Result>(SkRuntimeEffect::MakeForShader(edgeShader)); LOG_ALWAYS_FATAL_IF(!mResult->errorText.isEmpty(), "EdgeExtensionShaderFactory compilation " diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp index f262158f2c..6a577ff41f 100644 --- a/libs/renderengine/skia/filters/LutShader.cpp +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -24,7 +24,6 @@ #include <ui/ColorSpace.h> #include "include/core/SkColorSpace.h" -#include "src/core/SkColorFilterPriv.h" using aidl::android::hardware::graphics::composer3::LutProperties; @@ -116,7 +115,7 @@ static const SkString kShader = SkString(R"( linear = mix(c0, c1, linear.b); } } - return float4(linear, rgba.a); + return float4(fromLinearSrgb(linear), rgba.a); })"); // same as shader::toColorSpace function @@ -289,9 +288,7 @@ sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input, lutProperties[i].samplingKey, srcDataspace); } - auto colorXformLutToDst = - SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace); - input = input->makeWithColorFilter(colorXformLutToDst); + input = input->makeWithWorkingColorSpace(outColorSpace); } return input; } diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h index 800bf3c1d4..271d7c8563 100644 --- a/libs/tracing_perfetto/include/tracing_sdk.h +++ b/libs/tracing_perfetto/include/tracing_sdk.h @@ -292,13 +292,8 @@ class DebugArg { arg_ = std::move(arg); } - ~DebugArg() { - free_string_value(); - } - void set_value(T value) { if constexpr (std::is_same_v<T, const char*>) { - free_string_value(); arg_.value = value; } else if constexpr (std::is_same_v<T, int64_t>) { arg_.value = value; @@ -321,16 +316,6 @@ class DebugArg { DISALLOW_COPY_AND_ASSIGN(DebugArg); TypeMap::type arg_; const std::string name_; - - constexpr void free_string_value() { - if constexpr (std::is_same_v<typename TypeMap::type, - PerfettoTeHlExtraDebugArgString>) { - if (arg_.value) { - free((void*)arg_.value); - arg_.value = nullptr; - } - } - } }; template <typename T> @@ -375,10 +360,6 @@ class ProtoField { arg_ = std::move(arg); } - ~ProtoField() { - free_string_value(); - } - void set_value(uint32_t id, T value) { if constexpr (std::is_same_v<T, int64_t>) { arg_.header.id = id; @@ -387,7 +368,6 @@ class ProtoField { arg_.header.id = id; arg_.value = value; } else if constexpr (std::is_same_v<T, const char*>) { - free_string_value(); arg_.header.id = id; arg_.str = value; } @@ -404,16 +384,6 @@ class ProtoField { private: DISALLOW_COPY_AND_ASSIGN(ProtoField); TypeMap::type arg_; - - constexpr void free_string_value() { - if constexpr (std::is_same_v<typename TypeMap::type, - PerfettoTeHlProtoFieldCstr>) { - if (arg_.str) { - free((void*)arg_.str); - arg_.str = nullptr; - } - } - } }; class ProtoFieldNested { diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h index 3e1ac2a112..0e3fb07efd 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.h +++ b/libs/tracing_perfetto/tracing_perfetto_internal.h @@ -25,8 +25,6 @@ namespace tracing_perfetto { namespace internal { -bool isPerfettoRegistered(); - struct PerfettoTeCategory* toPerfettoCategory(uint64_t category); void registerWithPerfetto(bool test = false); diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp index c97e900952..70b8be981b 100644 --- a/libs/tracing_perfetto/tracing_sdk.cpp +++ b/libs/tracing_perfetto/tracing_sdk.cpp @@ -38,7 +38,7 @@ void trace_event(int type, const PerfettoTeCategory* perfettoTeCategory, PerfettoTeHlEmitImpl(perfettoTeCategory->impl, type, type == PERFETTO_TE_TYPE_COUNTER ? nullptr : name, extra->get()); - extra->pop_extra(); + extra->clear_extras(); } } diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 87e213e394..10cb992835 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -122,6 +122,7 @@ cc_library_shared { srcs: [ "DebugUtils.cpp", + "DependencyMonitor.cpp", "DeviceProductInfo.cpp", "DisplayIdentification.cpp", "DynamicDisplayInfo.cpp", diff --git a/libs/ui/DependencyMonitor.cpp b/libs/ui/DependencyMonitor.cpp new file mode 100644 index 0000000000..b7e490eba4 --- /dev/null +++ b/libs/ui/DependencyMonitor.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "DependencyMonitor" + +#include <ui/DependencyMonitor.h> +#include <ui/Fence.h> +#include <utils/Timers.h> + +#include <inttypes.h> + +namespace android { + +void DependencyMonitor::addIngress(FenceTimePtr fence, std::string annotation) { + std::lock_guard lock(mMutex); + resolveLocked(); + if (mDependencies.isFull() && !mDependencies.front().updateSignalTimes(true)) { + ALOGD("%s: Clobbering unresolved dependencies -- make me bigger!", mToken.c_str()); + } + + auto& entry = mDependencies.next(); + entry.reset(mToken.c_str()); + ALOGV("%" PRId64 "/%s: addIngress at CPU time %" PRId64 " (%s)", mDependencies.back().id, + mToken.c_str(), systemTime(), annotation.c_str()); + + mDependencies.back().ingress = {std::move(fence), std::move(annotation)}; +} + +void DependencyMonitor::addAccessCompletion(FenceTimePtr fence, std::string annotation) { + std::lock_guard lock(mMutex); + if (mDependencies.size() == 0) { + return; + } + ALOGV("%" PRId64 "/%s: addAccessCompletion at CPU time %" PRId64 " (%s)", + mDependencies.back().id, mToken.c_str(), systemTime(), annotation.c_str()); + mDependencies.back().accessCompletions.emplace_back(std::move(fence), std::move(annotation)); +} + +void DependencyMonitor::addEgress(FenceTimePtr fence, std::string annotation) { + std::lock_guard lock(mMutex); + if (mDependencies.size() == 0) { + return; + } + ALOGV("%" PRId64 "/%s: addEgress at CPU time %" PRId64 " (%s)", mDependencies.back().id, + mToken.c_str(), systemTime(), annotation.c_str()); + mDependencies.back().egress = {std::move(fence), std::move(annotation)}; +} + +void DependencyMonitor::resolveLocked() { + if (mDependencies.size() == 0) { + return; + } + + for (size_t i = mDependencies.size(); i > 0; i--) { + auto& dependencyBlock = mDependencies[i - 1]; + + if (dependencyBlock.validated) { + continue; + } + + if (!dependencyBlock.updateSignalTimes(false)) { + break; + } + + dependencyBlock.validated = true; + dependencyBlock.checkUnsafeAccess(); + } +} + +bool DependencyMonitor::DependencyBlock::updateSignalTimes(bool excludeIngress) { + if (egress.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) { + return false; + } + + if (!excludeIngress && ingress.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) { + return false; + } + + for (auto& accessCompletion : accessCompletions) { + if (accessCompletion.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) { + return false; + } + } + + return true; +} + +void DependencyMonitor::DependencyBlock::checkUnsafeAccess() const { + const nsecs_t egressTime = egress.fence->getCachedSignalTime(); + const nsecs_t ingressTime = ingress.fence->getCachedSignalTime(); + + ALOGV_IF(egressTime != Fence::SIGNAL_TIME_INVALID, + "%" PRId64 "/%s: Egress time: %" PRId64 " (%s)", token, id, egressTime, + egress.annotation.c_str()); + ALOGV_IF(Fence::isValidTimestamp(egressTime) && Fence::isValidTimestamp(ingressTime) && + egressTime < ingressTime, + "%" PRId64 "/%s: Detected egress before ingress!: %" PRId64 " (%s) < %" PRId64 " (%s)", + id, token, egressTime, egress.annotation, ingressTime, ingress.annotation.c_str()); + + for (auto& accessCompletion : accessCompletions) { + const nsecs_t accessCompletionTime = accessCompletion.fence->getCachedSignalTime(); + if (!Fence::isValidTimestamp(accessCompletionTime)) { + ALOGI("%" PRId64 "/%s: Detected invalid access completion! <%s>", id, token, + accessCompletion.annotation.c_str()); + continue; + } else { + ALOGV("%" PRId64 "/%s: Access completion time: %" PRId64 " <%s>", id, token, + accessCompletionTime, accessCompletion.annotation.c_str()); + } + + ALOGI_IF(Fence::isValidTimestamp(egressTime) && accessCompletionTime > egressTime, + "%" PRId64 "/%s: Detected access completion after egress!: %" PRId64 + " (%s) > %" PRId64 " (%s)", + id, token, accessCompletionTime, accessCompletion.annotation.c_str(), egressTime, + egress.annotation.c_str()); + + ALOGI_IF(Fence::isValidTimestamp(ingressTime) && accessCompletionTime < ingressTime, + "%" PRId64 "/%s: Detected access completion prior to ingress!: %" PRId64 + " (%s) < %" PRId64 " (%s)", + id, token, accessCompletionTime, accessCompletion.annotation.c_str(), ingressTime, + ingress.annotation.c_str()); + } + + ALOGV_IF(ingressTime != Fence::SIGNAL_TIME_INVALID, + "%" PRId64 "/%s: Ingress time: %" PRId64 " (%s)", id, token, ingressTime, + ingress.annotation.c_str()); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp index 4246c40f64..81afe9ef0e 100644 --- a/libs/ui/FenceTime.cpp +++ b/libs/ui/FenceTime.cpp @@ -59,6 +59,14 @@ FenceTime::FenceTime(nsecs_t signalTime) } } +FenceTimePtr FenceTime::makeValid(const sp<Fence>& fence) { + if (fence && fence->isValid()) { + return std::make_shared<FenceTime>(fence); + } else { + return std::make_shared<FenceTime>(systemTime()); + } +} + void FenceTime::applyTrustedSnapshot(const Snapshot& src) { if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) { // Applying Snapshot::State::FENCE, could change the valid state of the @@ -289,9 +297,10 @@ status_t FenceTime::Snapshot::unflatten( // ============================================================================ void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) { std::lock_guard<std::mutex> lock(mMutex); - while (mQueue.size() >= MAX_ENTRIES) { + static constexpr size_t MAX_QUEUE_SIZE = 64; + while (mQueue.size() >= MAX_QUEUE_SIZE) { // This is a sanity check to make sure the queue doesn't grow unbounded. - // MAX_ENTRIES should be big enough not to trigger this path. + // MAX_QUEUE_SIZE should be big enough not to trigger this path. // In case this path is taken though, users of FenceTime must make sure // not to rely solely on FenceTimeline to get the final timestamp and // should eventually call Fence::getSignalTime on their own. diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 18c9a6bc48..f7c94005f1 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -27,6 +27,8 @@ #include <ui/GraphicBufferMapper.h> #include <utils/Trace.h> +#include <string> + namespace android { // =========================================================================== @@ -104,6 +106,7 @@ GraphicBuffer::GraphicBuffer() usage = 0; layerCount = 0; handle = nullptr; + mDependencyMonitor.setToken(std::to_string(mId)); } // deprecated @@ -155,6 +158,8 @@ GraphicBuffer::GraphicBuffer(const GraphicBufferAllocator::AllocationRequest& re layerCount = request.layerCount; usage = request.usage; usage_deprecated = int(usage); + std::string name = request.requestorName; + mDependencyMonitor.setToken(name.append(":").append(std::to_string(mId))); } } @@ -252,6 +257,7 @@ status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight, usage = inUsage; usage_deprecated = int(usage); stride = static_cast<int>(outStride); + mDependencyMonitor.setToken(requestorName.append(":").append(std::to_string(mId))); } return err; } @@ -609,6 +615,14 @@ status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); } + std::string name; + status_t err = mBufferMapper.getName(handle, &name); + if (err != NO_ERROR) { + name = "<Unknown>"; + } + + mDependencyMonitor.setToken(name.append(":").append(std::to_string(mId))); + buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded); size -= sizeNeeded; fds += numFds; diff --git a/libs/ui/include/ui/DependencyMonitor.h b/libs/ui/include/ui/DependencyMonitor.h new file mode 100644 index 0000000000..5ad1fd9528 --- /dev/null +++ b/libs/ui/include/ui/DependencyMonitor.h @@ -0,0 +1,104 @@ +/* + * Copyright 2025 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/FatVector.h> +#include <ui/FenceTime.h> +#include <ui/RingBuffer.h> + +namespace android { + +// Debugging class for that tries to add userspace logging for fence depencencies. +// The model that a DependencyMonitor tries to follow is, for each access of some resource: +// 1. There is a single ingress fence, that guards whether a resource is now safe to read from +// another system. +// 2. There are multiple access fences, that are fired when a resource is read. +// 3. There is a single egress fence, that is fired when a resource is released and sent to another +// system. +// +// Note that there can be repeated ingress and egress of a resource, but the assumption is that +// there is exactly one egress for every ingress, unless the resource is destroyed rather than +// released. +// +// The DependencyMonitor will log if there is an anomaly in the fences tracked for some resource. +// This includes: +// * If (2) happens before (1) +// * If (2) happens after (3) +// +// Note that this class has no knowledge of the "other system". I.e., if the other system ignores +// the fence reported in (3), but still takes a long time to write to the resource and produce (1), +// then nothing will be logged. That other system must have its own DependencyMonitor. Conversely, +// this class has imperfect knowledge of the system it is monitoring. For example, this class does +// not know the precise start times of reading from a resource, the exact time that a read might +// occur from a hardware unit is not known to userspace. +// +// In other words, this class logs specific classes of fence violations, but is not sensitive to +// *all* violations. One property of this is that unless the system tracked by a DependencyMonitor +// is feeding in literally incorrect fences, then there is no chance of a false positive. +// +// This class is thread safe. +class DependencyMonitor { +public: + // Sets a debug token identifying the resource this monitor is tracking. + void setToken(std::string token) { mToken = std::move(token); } + + // Adds a fence that is fired when the resource ready to be ingested by the system using the + // DependencyMonitor. + void addIngress(FenceTimePtr fence, std::string annotation); + // Adds a fence that is fired when the resource is accessed. + void addAccessCompletion(FenceTimePtr fence, std::string annotation); + // Adds a fence that is fired when the resource is released to another system. + void addEgress(FenceTimePtr fence, std::string annotation); + +private: + struct AnnotatedFenceTime { + FenceTimePtr fence; + std::string annotation; + }; + + struct DependencyBlock { + int64_t id = -1; + AnnotatedFenceTime ingress = {FenceTime::NO_FENCE, ""}; + FatVector<AnnotatedFenceTime> accessCompletions; + AnnotatedFenceTime egress = {FenceTime::NO_FENCE, ""}; + bool validated = false; + const char* token = nullptr; + + void reset(const char* newToken) { + static std::atomic<int64_t> counter = 0; + id = counter++; + ingress = {FenceTime::NO_FENCE, ""}; + accessCompletions.clear(); + egress = {FenceTime::NO_FENCE, ""}; + validated = false; + token = newToken; + } + + // Returns true if all fences in this block have valid signal times. + bool updateSignalTimes(bool excludeIngress); + + void checkUnsafeAccess() const; + }; + + void resolveLocked() REQUIRES(mMutex); + + std::string mToken; + std::mutex mMutex; + ui::RingBuffer<DependencyBlock, 10> mDependencies GUARDED_BY(mMutex); +}; + +} // namespace android
\ No newline at end of file diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h index 937e3f1486..1e1c77b9fc 100644 --- a/libs/ui/include/ui/DisplayId.h +++ b/libs/ui/include/ui/DisplayId.h @@ -20,6 +20,7 @@ #include <ostream> #include <string> +#include <ftl/match.h> #include <ftl/optional.h> namespace android { @@ -36,7 +37,6 @@ struct DisplayId { DisplayId& operator=(const DisplayId&) = default; static constexpr DisplayId fromValue(uint64_t value) { return DisplayId(value); } - constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; } uint64_t value; @@ -66,13 +66,6 @@ struct PhysicalDisplayId : DisplayId { // TODO: b/162612135 - Remove default constructor. PhysicalDisplayId() = default; - static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) { - if (id.isVirtual()) { - return std::nullopt; - } - return PhysicalDisplayId(id); - } - // Returns a stable ID based on EDID and port information. static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) { @@ -90,8 +83,6 @@ struct PhysicalDisplayId : DisplayId { return PhysicalDisplayId(value); } - constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); } - private: // Flag indicating that the ID is stable across reboots. static constexpr uint64_t FLAG_STABLE = 1ULL << 62; @@ -112,13 +103,6 @@ struct VirtualDisplayId : DisplayId { // Flag indicating that this virtual display is backed by the GPU. static constexpr uint64_t FLAG_GPU = 1ULL << 61; - static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) { - if (id.isVirtual()) { - return VirtualDisplayId(id); - } - return std::nullopt; - } - static constexpr VirtualDisplayId fromValue(uint64_t value) { return VirtualDisplayId(SkipVirtualFlag{}, value); } @@ -134,13 +118,6 @@ protected: struct HalVirtualDisplayId : VirtualDisplayId { explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(baseId) {} - static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) { - if (id.isVirtual() && !(id.value & FLAG_GPU)) { - return HalVirtualDisplayId(id); - } - return std::nullopt; - } - static constexpr HalVirtualDisplayId fromValue(uint64_t value) { return HalVirtualDisplayId(SkipVirtualFlag{}, value); } @@ -152,13 +129,6 @@ private: struct GpuVirtualDisplayId : VirtualDisplayId { explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {} - static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) { - if (id.isVirtual() && (id.value & FLAG_GPU)) { - return GpuVirtualDisplayId(id); - } - return std::nullopt; - } - static constexpr GpuVirtualDisplayId fromValue(uint64_t value) { return GpuVirtualDisplayId(SkipVirtualFlag{}, value); } @@ -172,14 +142,6 @@ private: struct HalDisplayId : DisplayId { constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {} constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {} - - static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) { - if (GpuVirtualDisplayId::tryCast(id)) { - return std::nullopt; - } - return HalDisplayId(id); - } - static constexpr HalDisplayId fromValue(uint64_t value) { return HalDisplayId(value); } private: @@ -187,6 +149,47 @@ private: explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {} }; +using DisplayIdVariant = std::variant<PhysicalDisplayId, GpuVirtualDisplayId, HalVirtualDisplayId>; +using VirtualDisplayIdVariant = std::variant<GpuVirtualDisplayId, HalVirtualDisplayId>; + +template <typename DisplayIdType> +inline auto asDisplayIdOfType(DisplayIdVariant variant) -> ftl::Optional<DisplayIdType> { + return ftl::match( + variant, + [](DisplayIdType id) -> ftl::Optional<DisplayIdType> { return ftl::Optional(id); }, + [](auto) -> ftl::Optional<DisplayIdType> { return std::nullopt; }); +} + +template <typename Variant> +inline auto asHalDisplayId(Variant variant) -> ftl::Optional<HalDisplayId> { + return ftl::match( + variant, + [](GpuVirtualDisplayId) -> ftl::Optional<HalDisplayId> { return std::nullopt; }, + [](auto id) -> ftl::Optional<HalDisplayId> { + return ftl::Optional(static_cast<HalDisplayId>(id)); + }); +} + +inline auto asPhysicalDisplayId(DisplayIdVariant variant) -> ftl::Optional<PhysicalDisplayId> { + return asDisplayIdOfType<PhysicalDisplayId>(variant); +} + +inline auto asVirtualDisplayId(DisplayIdVariant variant) -> ftl::Optional<VirtualDisplayId> { + return ftl::match( + variant, + [](GpuVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> { + return ftl::Optional(static_cast<VirtualDisplayId>(id)); + }, + [](HalVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> { + return ftl::Optional(static_cast<VirtualDisplayId>(id)); + }, + [](auto) -> ftl::Optional<VirtualDisplayId> { return std::nullopt; }); +} + +inline auto asDisplayId(DisplayIdVariant variant) -> DisplayId { + return ftl::match(variant, [](auto id) -> DisplayId { return static_cast<DisplayId>(id); }); +} + static_assert(sizeof(DisplayId) == sizeof(uint64_t)); static_assert(sizeof(HalDisplayId) == sizeof(uint64_t)); static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t)); diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h index 334106f0cf..3560d57cff 100644 --- a/libs/ui/include/ui/FenceTime.h +++ b/libs/ui/include/ui/FenceTime.h @@ -17,6 +17,7 @@ #ifndef ANDROID_FENCE_TIME_H #define ANDROID_FENCE_TIME_H +#include <stddef.h> #include <ui/Fence.h> #include <utils/Flattenable.h> #include <utils/Mutex.h> @@ -30,6 +31,8 @@ namespace android { class FenceToFenceTimeMap; +class FenceTime; +using FenceTimePtr = std::shared_ptr<FenceTime>; // A wrapper around fence that only implements isValid and getSignalTime. // It automatically closes the fence in a thread-safe manner once the signal @@ -95,6 +98,10 @@ public: FenceTime& operator=(const FenceTime&) = delete; FenceTime& operator=(FenceTime&&) = delete; + // Constructs a FenceTime, falling back to a timestamp if the fence is + // invalid. + static FenceTimePtr makeValid(const sp<Fence>& fence); + // This method should only be called when replacing the fence with // a signalTime. Since this is an indirect way of setting the signal time // of a fence, the snapshot should come from a trusted source. @@ -142,8 +149,6 @@ private: std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID}; }; -using FenceTimePtr = std::shared_ptr<FenceTime>; - // A queue of FenceTimes that are expected to signal in FIFO order. // Only maintains a queue of weak pointers so it doesn't keep references // to Fences on its own. @@ -162,8 +167,6 @@ using FenceTimePtr = std::shared_ptr<FenceTime>; // different threads. class FenceTimeline { public: - static constexpr size_t MAX_ENTRIES = 64; - void push(const std::shared_ptr<FenceTime>& fence); void updateSignalTimes(); diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index 936bf8f862..9305180ecb 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -23,6 +23,7 @@ #include <string> #include <utility> #include <vector> +#include "ui/DependencyMonitor.h" #include <android/hardware_buffer.h> #include <ui/ANativeObjectBase.h> @@ -229,6 +230,8 @@ public: void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context); + DependencyMonitor& getDependencyMonitor() { return mDependencyMonitor; } + private: ~GraphicBuffer(); @@ -295,6 +298,8 @@ private: // and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer. std::vector<std::pair<GraphicBufferDeathCallback, void* /*mDeathCallbackContext*/>> mDeathCallbacks; + + DependencyMonitor mDependencyMonitor; }; } // namespace android diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/libs/ui/include/ui/RingBuffer.h index 215472b388..31d5a950ef 100644 --- a/services/surfaceflinger/Utils/RingBuffer.h +++ b/libs/ui/include/ui/RingBuffer.h @@ -19,7 +19,7 @@ #include <stddef.h> #include <array> -namespace android::utils { +namespace android::ui { template <class T, size_t SIZE> class RingBuffer { @@ -31,8 +31,8 @@ public: ~RingBuffer() = default; constexpr size_t capacity() const { return SIZE; } - size_t size() const { return mCount; } + bool isFull() const { return size() == capacity(); } T& next() { mHead = static_cast<size_t>(mHead + 1) % SIZE; @@ -67,4 +67,4 @@ private: size_t mCount = 0; }; -} // namespace android::utils +} // namespace android::ui diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h index 83da821f37..53164487f3 100644 --- a/libs/ui/include/ui/StaticDisplayInfo.h +++ b/libs/ui/include/ui/StaticDisplayInfo.h @@ -28,6 +28,7 @@ enum class DisplayConnectionType { Internal, External, ftl_last = External }; // Immutable information about physical display. struct StaticDisplayInfo { DisplayConnectionType connectionType = DisplayConnectionType::Internal; + uint8_t port; float density = 0.f; bool secure = false; std::optional<DeviceProductInfo> deviceProductInfo; diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h index 70c50f07e5..98018d95d1 100644 --- a/libs/ui/include_types/ui/HdrRenderTypeUtils.h +++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h @@ -36,7 +36,7 @@ enum class HdrRenderType { */ inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace, std::optional<ui::PixelFormat> pixelFormat, - float hdrSdrRatio = 1.f) { + float hdrSdrRatio = 1.f, bool hasHdrMetadata = false) { const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK; const auto range = dataspace & HAL_DATASPACE_RANGE_MASK; @@ -49,7 +49,8 @@ inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace, HAL_DATASPACE_RANGE_EXTENDED); if ((dataspace == BT2020_LINEAR_EXT || dataspace == ui::Dataspace::V0_SCRGB) && - pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16) { + pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16 && + hasHdrMetadata) { return HdrRenderType::GENERIC_HDR; } diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 2d8a1e326c..d950f2a23f 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -45,16 +45,6 @@ cc_test { } cc_test { - name: "DisplayId_test", - shared_libs: ["libui"], - srcs: ["DisplayId_test.cpp"], - cflags: [ - "-Wall", - "-Werror", - ], -} - -cc_test { name: "DisplayIdentification_test", shared_libs: ["libui"], static_libs: ["libgmock"], @@ -144,6 +134,17 @@ cc_test { } cc_test { + name: "RingBuffer_test", + test_suites: ["device-tests"], + shared_libs: ["libui"], + srcs: ["RingBuffer_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], +} + +cc_test { name: "Size_test", test_suites: ["device-tests"], shared_libs: ["libui"], diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp deleted file mode 100644 index 209acba672..0000000000 --- a/libs/ui/tests/DisplayId_test.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <ui/DisplayId.h> - -#include <gtest/gtest.h> - -namespace android::ui { - -TEST(DisplayIdTest, createPhysicalIdFromEdid) { - constexpr uint8_t port = 1; - constexpr uint16_t manufacturerId = 13; - constexpr uint32_t modelHash = 42; - const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash); - EXPECT_EQ(port, id.getPort()); - EXPECT_FALSE(VirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); - EXPECT_TRUE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, PhysicalDisplayId::fromValue(id.value)); -} - -TEST(DisplayIdTest, createPhysicalIdFromPort) { - constexpr uint8_t port = 3; - const PhysicalDisplayId id = PhysicalDisplayId::fromPort(port); - EXPECT_EQ(port, id.getPort()); - EXPECT_FALSE(VirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); - EXPECT_TRUE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, PhysicalDisplayId::fromValue(id.value)); -} - -TEST(DisplayIdTest, createGpuVirtualId) { - const GpuVirtualDisplayId id(42); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_FALSE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, GpuVirtualDisplayId::fromValue(id.value)); -} - -TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) { - const VirtualDisplayId id(GpuVirtualDisplayId(42)); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_FALSE(HalDisplayId::tryCast(id)); - - const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU); - EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value()); -} - -TEST(DisplayIdTest, createHalVirtualId) { - const HalVirtualDisplayId id(42); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_TRUE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, HalVirtualDisplayId::fromValue(id.value)); -} - -TEST(DisplayIdTest, createVirtualIdFromHalVirtualId) { - const VirtualDisplayId id(HalVirtualDisplayId(42)); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_TRUE(HalDisplayId::tryCast(id)); - - const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU); - EXPECT_EQ((id.isVirtual() && !isGpuVirtualId), HalVirtualDisplayId::tryCast(id).has_value()); -} - -} // namespace android::ui diff --git a/libs/ui/tests/RingBuffer_test.cpp b/libs/ui/tests/RingBuffer_test.cpp new file mode 100644 index 0000000000..9839492b9f --- /dev/null +++ b/libs/ui/tests/RingBuffer_test.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <ui/RingBuffer.h> + +namespace android::ui { + +TEST(RingBuffer, basic) { + RingBuffer<int, 5> rb; + + rb.next() = 1; + ASSERT_EQ(1, rb.size()); + ASSERT_EQ(1, rb.back()); + ASSERT_EQ(1, rb.front()); + + rb.next() = 2; + ASSERT_EQ(2, rb.size()); + ASSERT_EQ(2, rb.back()); + ASSERT_EQ(1, rb.front()); + ASSERT_EQ(1, rb[-1]); + + rb.next() = 3; + ASSERT_EQ(3, rb.size()); + ASSERT_EQ(3, rb.back()); + ASSERT_EQ(1, rb.front()); + ASSERT_EQ(2, rb[-1]); + ASSERT_EQ(1, rb[-2]); + + rb.next() = 4; + ASSERT_EQ(4, rb.size()); + ASSERT_EQ(4, rb.back()); + ASSERT_EQ(1, rb.front()); + ASSERT_EQ(3, rb[-1]); + ASSERT_EQ(2, rb[-2]); + ASSERT_EQ(1, rb[-3]); + + rb.next() = 5; + ASSERT_EQ(5, rb.size()); + ASSERT_EQ(5, rb.back()); + ASSERT_EQ(1, rb.front()); + ASSERT_EQ(4, rb[-1]); + ASSERT_EQ(3, rb[-2]); + ASSERT_EQ(2, rb[-3]); + ASSERT_EQ(1, rb[-4]); + + rb.next() = 6; + ASSERT_EQ(5, rb.size()); + ASSERT_EQ(6, rb.back()); + ASSERT_EQ(2, rb.front()); + ASSERT_EQ(5, rb[-1]); + ASSERT_EQ(4, rb[-2]); + ASSERT_EQ(3, rb[-3]); + ASSERT_EQ(2, rb[-4]); +} + +} // namespace android::ui
\ No newline at end of file diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp index 04c525e93d..7faf361c08 100644 --- a/opengl/libs/EGL/MultifileBlobCache.cpp +++ b/opengl/libs/EGL/MultifileBlobCache.cpp @@ -356,7 +356,7 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi // If we're going to be over the cache limit, kick off a trim to clear space if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) { - ALOGV("SET: Cache is full, calling trimCache to clear space"); + ALOGW("SET: Cache is full, calling trimCache to clear space"); trimCache(); } diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 0dd9f198e5..efc34f6aa8 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -146,7 +146,7 @@ static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_conn } } - if (graphicsenv_flags::feature_overrides()) { + if (graphicsenv_flags::angle_feature_overrides()) { // Get the list of ANGLE features to enable from Global.Settings. const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures(); for (const std::string& eglFeature : eglFeatures) { diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp index e94b565f11..beac9001bc 100644 --- a/opengl/tests/lib/WindowSurface.cpp +++ b/opengl/tests/lib/WindowSurface.cpp @@ -29,7 +29,7 @@ using namespace android; WindowSurface::WindowSurface() { status_t err; - sp<SurfaceComposerClient> surfaceComposerClient = new SurfaceComposerClient; + sp<SurfaceComposerClient> surfaceComposerClient = sp<SurfaceComposerClient>::make(); err = surfaceComposerClient->initCheck(); if (err != NO_ERROR) { fprintf(stderr, "SurfaceComposerClient::initCheck error: %#x\n", err); diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp index afa623352b..56c3b7d31f 100644 --- a/services/automotive/display/AutomotiveDisplayProxyService.cpp +++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp @@ -65,7 +65,7 @@ AutomotiveDisplayProxyService::getIGraphicBufferProducer(uint64_t id) { std::swap(displayWidth, displayHeight); } - sp<android::SurfaceComposerClient> surfaceClient = new SurfaceComposerClient(); + sp<android::SurfaceComposerClient> surfaceClient = sp<SurfaceComposerClient>::make(); err = surfaceClient->initCheck(); if (err != NO_ERROR) { ALOGE("SurfaceComposerClient::initCheck error: %#x", err); diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index 01287b08f4..74e354f09c 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -39,6 +39,7 @@ cc_aconfig_library { cc_defaults { name: "libgpuservice_defaults", defaults: [ + "aconfig_lib_cc_static_link.defaults", "gpuservice_defaults", "libfeatureoverride_deps", "libgfxstats_deps", diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index f74b4fa34d..46327dfbe7 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -25,8 +25,10 @@ #include <binder/Parcel.h> #include <binder/PermissionCache.h> #include <com_android_frameworks_gpuservice_flags.h> +#include <com_android_graphics_graphicsenv_flags.h> #include <cutils/properties.h> #include <cutils/multiuser.h> +#include <feature_override/FeatureOverrideParser.h> #include <gpumem/GpuMem.h> #include <gpuwork/GpuWork.h> #include <gpustats/GpuStats.h> @@ -41,6 +43,7 @@ #include <memory> namespace gpuservice_flags = com::android::frameworks::gpuservice::flags; +namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags; namespace android { @@ -143,6 +146,14 @@ void GpuService::toggleAngleAsSystemDriver(bool enabled) { } } +FeatureOverrides GpuService::getFeatureOverrides() { + if (!graphicsenv_flags::angle_feature_overrides()) { + FeatureOverrides featureOverrides; + return featureOverrides; + } + + return mFeatureOverrideParser.getFeatureOverrides(); +} void GpuService::setUpdatableDriverPath(const std::string& driverPath) { IPCThreadState* ipc = IPCThreadState::self(); @@ -171,7 +182,11 @@ status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<Stri for (size_t i = 0, n = args.size(); i < n; i++) ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).c_str()); - if (args.size() >= 1) { + if (!args.empty()) { + if (graphicsenv_flags::angle_feature_overrides()) { + if (args[0] == String16("featureOverrides")) + return cmdFeatureOverrides(out, err); + } 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); @@ -235,6 +250,11 @@ status_t GpuService::doDump(int fd, const Vector<String16>& args, bool /*asProto return NO_ERROR; } +status_t GpuService::cmdFeatureOverrides(int out, int /*err*/) { + dprintf(out, "%s\n", mFeatureOverrideParser.getFeatureOverrides().toString().c_str()); + return NO_ERROR; +} + namespace { status_t cmdHelp(int out) { @@ -247,6 +267,10 @@ status_t cmdHelp(int out) { "GPU Service commands:\n" " vkjson dump Vulkan properties as JSON\n" " vkprofiles print support for select Vulkan profiles\n"); + if (graphicsenv_flags::angle_feature_overrides()) { + fprintf(outs, + " featureOverrides update and output gpuservice's feature overrides\n"); + } fclose(outs); return NO_ERROR; } diff --git a/services/gpuservice/feature_override/Android.bp b/services/gpuservice/feature_override/Android.bp index 3b5407bdd3..842a0c46d2 100644 --- a/services/gpuservice/feature_override/Android.bp +++ b/services/gpuservice/feature_override/Android.bp @@ -69,6 +69,7 @@ cc_library_static { name: "libfeatureoverride", defaults: [ "libfeatureoverride_deps", + "libvkjson_deps", ], srcs: [ ":feature_config_proto_definitions", @@ -85,6 +86,9 @@ cc_library_static { cppflags: [ "-Wno-sign-compare", ], + static_libs: [ + "libvkjson", + ], export_include_dirs: ["include"], proto: { type: "lite", diff --git a/services/gpuservice/feature_override/FeatureOverrideParser.cpp b/services/gpuservice/feature_override/FeatureOverrideParser.cpp index a16bfa8b35..26ff84a34c 100644 --- a/services/gpuservice/feature_override/FeatureOverrideParser.cpp +++ b/services/gpuservice/feature_override/FeatureOverrideParser.cpp @@ -23,8 +23,10 @@ #include <sys/stat.h> #include <vector> +#include <android-base/macros.h> #include <graphicsenv/FeatureOverrides.h> #include <log/log.h> +#include <vkjson.h> #include "feature_config.pb.h" @@ -35,13 +37,53 @@ void resetFeatureOverrides(android::FeatureOverrides &featureOverrides) { featureOverrides.mPackageFeatures.clear(); } +bool +gpuVendorIdMatches(const VkJsonInstance &vkJsonInstance, + const uint32_t &configVendorId) { + if (vkJsonInstance.devices.empty()) { + return false; + } + + // Always match the TEST Vendor ID + if (configVendorId == feature_override::GpuVendorID::VENDOR_ID_TEST) { + return true; + } + + // Always assume one GPU device. + uint32_t vendorID = vkJsonInstance.devices.front().properties.vendorID; + + return vendorID == configVendorId; +} + +bool +conditionsMet(const VkJsonInstance &vkJsonInstance, + const android::FeatureConfig &featureConfig) { + bool gpuVendorIdMatch = false; + + if (featureConfig.mGpuVendorIDs.empty()) { + gpuVendorIdMatch = true; + } else { + for (const auto &gpuVendorID: featureConfig.mGpuVendorIDs) { + if (gpuVendorIdMatches(vkJsonInstance, gpuVendorID)) { + gpuVendorIdMatch = true; + break; + } + } + } + + return gpuVendorIdMatch; +} + void initFeatureConfig(android::FeatureConfig &featureConfig, const feature_override::FeatureConfig &featureConfigProto) { featureConfig.mFeatureName = featureConfigProto.feature_name(); featureConfig.mEnabled = featureConfigProto.enabled(); + for (const auto &gpuVendorIdProto: featureConfigProto.gpu_vendor_ids()) { + featureConfig.mGpuVendorIDs.emplace_back(static_cast<uint32_t>(gpuVendorIdProto)); + } } -feature_override::FeatureOverrideProtos readFeatureConfigProtos(std::string configFilePath) { +feature_override::FeatureOverrideProtos readFeatureConfigProtos(const std::string &configFilePath) { feature_override::FeatureOverrideProtos overridesProtos; std::ifstream protobufBinaryFile(configFilePath.c_str()); @@ -78,9 +120,15 @@ std::string FeatureOverrideParser::getFeatureOverrideFilePath() const { bool FeatureOverrideParser::shouldReloadFeatureOverrides() const { std::string configFilePath = getFeatureOverrideFilePath(); + + std::ifstream configFile(configFilePath); + if (!configFile.good()) { + return false; + } + struct stat fileStat{}; - if (stat(getFeatureOverrideFilePath().c_str(), &fileStat) != 0) { - ALOGE("Error getting file information for '%s': %s", getFeatureOverrideFilePath().c_str(), + if (stat(configFilePath.c_str(), &fileStat) != 0) { + ALOGE("Error getting file information for '%s': %s", configFilePath.c_str(), strerror(errno)); // stat'ing the file failed, so return false since reading it will also likely fail. return false; @@ -100,12 +148,22 @@ void FeatureOverrideParser::parseFeatureOverrides() { // Clear out the stale values before adding the newly parsed data. resetFeatureOverrides(mFeatureOverrides); + if (overridesProtos.global_features().empty() && + overridesProtos.package_features().empty()) { + // No overrides to parse. + return; + } + + const VkJsonInstance vkJsonInstance = VkJsonGetInstance(); + // Global feature overrides. for (const auto &featureConfigProto: overridesProtos.global_features()) { FeatureConfig featureConfig; initFeatureConfig(featureConfig, featureConfigProto); - mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig); + if (conditionsMet(vkJsonInstance, featureConfig)) { + mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig); + } } // App-specific feature overrides. @@ -122,7 +180,9 @@ void FeatureOverrideParser::parseFeatureOverrides() { FeatureConfig featureConfig; initFeatureConfig(featureConfig, featureConfigProto); - featureConfigs.emplace_back(featureConfig); + if (conditionsMet(vkJsonInstance, featureConfig)) { + featureConfigs.emplace_back(featureConfig); + } } mFeatureOverrides.mPackageFeatures[packageName] = featureConfigs; diff --git a/services/gpuservice/feature_override/proto/feature_config.proto b/services/gpuservice/feature_override/proto/feature_config.proto index 4d4bf289f1..f28518778f 100644 --- a/services/gpuservice/feature_override/proto/feature_config.proto +++ b/services/gpuservice/feature_override/proto/feature_config.proto @@ -21,14 +21,43 @@ package feature_override; option optimize_for = LITE_RUNTIME; /** + * GPU Vendor IDs. + * Taken from: external/angle/src/libANGLE/renderer/driver_utils.h + */ +enum GpuVendorID +{ + // Test ID matches every GPU Vendor ID. + VENDOR_ID_TEST = 0; + VENDOR_ID_AMD = 0x1002; + VENDOR_ID_ARM = 0x13B5; + // Broadcom devices won't use PCI, but this is their Vulkan vendor id. + VENDOR_ID_BROADCOM = 0x14E4; + VENDOR_ID_GOOGLE = 0x1AE0; + VENDOR_ID_INTEL = 0x8086; + VENDOR_ID_MESA = 0x10005; + VENDOR_ID_MICROSOFT = 0x1414; + VENDOR_ID_NVIDIA = 0x10DE; + VENDOR_ID_POWERVR = 0x1010; + // This is Qualcomm PCI Vendor ID. + // Android doesn't have a PCI bus, but all we need is a unique id. + VENDOR_ID_QUALCOMM = 0x5143; + VENDOR_ID_SAMSUNG = 0x144D; + VENDOR_ID_VIVANTE = 0x9999; + VENDOR_ID_VMWARE = 0x15AD; + VENDOR_ID_VIRTIO = 0x1AF4; +} + +/** * Feature Configuration * feature_name: Feature name (see external/angle/include/platform/autogen/FeaturesVk_autogen.h). * enabled: Either enable or disable the feature. + * gpu_vendor_ids: The GPU architectures this FeatureConfig applies to, if any. */ message FeatureConfig { string feature_name = 1; bool enabled = 2; + repeated GpuVendorID gpu_vendor_ids = 3; } /** diff --git a/services/gpuservice/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h index 057d127390..22be9a72b9 100644 --- a/services/gpuservice/include/gpuservice/GpuService.h +++ b/services/gpuservice/include/gpuservice/GpuService.h @@ -20,6 +20,7 @@ #include <binder/IInterface.h> #include <cutils/compiler.h> #include <feature_override/FeatureOverrideParser.h> +#include <graphicsenv/FeatureOverrides.h> #include <graphicsenv/GpuStatsInfo.h> #include <graphicsenv/IGpuService.h> #include <serviceutils/PriorityDumper.h> @@ -64,6 +65,7 @@ private: const uint64_t* values, const uint32_t valueCount) override; void setUpdatableDriverPath(const std::string& driverPath) override; std::string getUpdatableDriverPath() override; + FeatureOverrides getFeatureOverrides() override; void toggleAngleAsSystemDriver(bool enabled) override; void addVulkanEngineName(const std::string& appPackageName, const uint64_t driverVersionCode, const char *engineName) override; @@ -86,6 +88,8 @@ private: status_t doDump(int fd, const Vector<String16>& args, bool asProto); + status_t cmdFeatureOverrides(int out, int /*err*/); + /* * Attributes */ diff --git a/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp index 65a1b58dc3..66556cd2d0 100644 --- a/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp +++ b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp @@ -70,14 +70,14 @@ public: }; testing::AssertionResult validateFeatureConfigTestTxtpbSizes(FeatureOverrides overrides) { - size_t expectedGlobalFeaturesSize = 1; + size_t expectedGlobalFeaturesSize = 3; if (overrides.mGlobalFeatures.size() != expectedGlobalFeaturesSize) { return testing::AssertionFailure() << "overrides.mGlobalFeatures.size(): " << overrides.mGlobalFeatures.size() << ", expected: " << expectedGlobalFeaturesSize; } - size_t expectedPackageFeaturesSize = 1; + size_t expectedPackageFeaturesSize = 3; if (overrides.mPackageFeatures.size() != expectedPackageFeaturesSize) { return testing::AssertionFailure() << "overrides.mPackageFeatures.size(): " << overrides.mPackageFeatures.size() @@ -133,6 +133,96 @@ TEST_F(FeatureOverrideParserTest, globalOverrides1) { EXPECT_TRUE(validateGlobalOverrides1(overrides)); } +testing::AssertionResult validateGlobalOverrides2(FeatureOverrides overrides) { + const int kTestFeatureIndex = 1; + const std::string expectedFeatureName = "globalOverrides2"; + const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex]; + + if (cfg.mFeatureName != expectedFeatureName) { + return testing::AssertionFailure() + << "cfg.mFeatureName: " << cfg.mFeatureName + << ", expected: " << expectedFeatureName; + } + + bool expectedEnabled = true; + if (cfg.mEnabled != expectedEnabled) { + return testing::AssertionFailure() + << "cfg.mEnabled: " << cfg.mEnabled + << ", expected: " << expectedEnabled; + } + + std::vector<uint32_t> expectedGpuVendorIDs = { + 0, // GpuVendorID::VENDOR_ID_TEST + 0x13B5, // GpuVendorID::VENDOR_ID_ARM + }; + if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) { + return testing::AssertionFailure() + << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size() + << ", expected: " << expectedGpuVendorIDs.size(); + } + for (int i = 0; i < expectedGpuVendorIDs.size(); i++) { + if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) { + std::stringstream msg; + msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i] + << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i]; + return testing::AssertionFailure() << msg.str(); + } + } + + return testing::AssertionSuccess(); +} + +TEST_F(FeatureOverrideParserTest, globalOverrides2) { + FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides(); + + EXPECT_TRUE(validateGlobalOverrides2(overrides)); +} + +testing::AssertionResult validateGlobalOverrides3(FeatureOverrides overrides) { + const int kTestFeatureIndex = 2; + const std::string expectedFeatureName = "globalOverrides3"; + const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex]; + + if (cfg.mFeatureName != expectedFeatureName) { + return testing::AssertionFailure() + << "cfg.mFeatureName: " << cfg.mFeatureName + << ", expected: " << expectedFeatureName; + } + + bool expectedEnabled = true; + if (cfg.mEnabled != expectedEnabled) { + return testing::AssertionFailure() + << "cfg.mEnabled: " << cfg.mEnabled + << ", expected: " << expectedEnabled; + } + + std::vector<uint32_t> expectedGpuVendorIDs = { + 0, // GpuVendorID::VENDOR_ID_TEST + 0x8086, // GpuVendorID::VENDOR_ID_INTEL + }; + if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) { + return testing::AssertionFailure() + << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size() + << ", expected: " << expectedGpuVendorIDs.size(); + } + for (int i = 0; i < expectedGpuVendorIDs.size(); i++) { + if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) { + std::stringstream msg; + msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i] + << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i]; + return testing::AssertionFailure() << msg.str(); + } + } + + return testing::AssertionSuccess(); +} + +TEST_F(FeatureOverrideParserTest, globalOverrides3) { +FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides(); + +EXPECT_TRUE(validateGlobalOverrides3(overrides)); +} + testing::AssertionResult validatePackageOverrides1(FeatureOverrides overrides) { const std::string expectedTestPackageName = "com.gpuservice_unittest.packageOverrides1"; @@ -155,6 +245,12 @@ testing::AssertionResult validatePackageOverrides1(FeatureOverrides overrides) { const std::string expectedFeatureName = "packageOverrides1"; const FeatureConfig &cfg = features[0]; + if (cfg.mFeatureName != expectedFeatureName) { + return testing::AssertionFailure() + << "cfg.mFeatureName: " << cfg.mFeatureName + << ", expected: " << expectedFeatureName; + } + bool expectedEnabled = true; if (cfg.mEnabled != expectedEnabled) { return testing::AssertionFailure() @@ -193,6 +289,160 @@ testing::AssertionResult validateForceFileRead(FeatureOverrides overrides) { return testing::AssertionSuccess(); } +testing::AssertionResult validatePackageOverrides2(FeatureOverrides overrides) { + const std::string expectedPackageName = "com.gpuservice_unittest.packageOverrides2"; + + if (!overrides.mPackageFeatures.count(expectedPackageName)) { + return testing::AssertionFailure() + << "overrides.mPackageFeatures missing expected package: " << expectedPackageName; + } + + const std::vector<FeatureConfig>& features = overrides.mPackageFeatures[expectedPackageName]; + + size_t expectedFeaturesSize = 1; + if (features.size() != expectedFeaturesSize) { + return testing::AssertionFailure() + << "features.size(): " << features.size() + << ", expectedFeaturesSize: " << expectedFeaturesSize; + } + + const std::string expectedFeatureName = "packageOverrides2"; + const FeatureConfig &cfg = features[0]; + + if (cfg.mFeatureName != expectedFeatureName) { + return testing::AssertionFailure() + << "cfg.mFeatureName: " << cfg.mFeatureName + << ", expected: " << expectedFeatureName; + } + + bool expectedEnabled = false; + if (cfg.mEnabled != expectedEnabled) { + return testing::AssertionFailure() + << "cfg.mEnabled: " << cfg.mEnabled + << ", expected: " << expectedEnabled; + } + + std::vector<uint32_t> expectedGpuVendorIDs = { + 0, // GpuVendorID::VENDOR_ID_TEST + 0x8086, // GpuVendorID::VENDOR_ID_INTEL + }; + if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) { + return testing::AssertionFailure() + << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size() + << ", expected: " << expectedGpuVendorIDs.size(); + } + for (int i = 0; i < expectedGpuVendorIDs.size(); i++) { + if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) { + std::stringstream msg; + msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i] + << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i]; + return testing::AssertionFailure() << msg.str(); + } + } + + return testing::AssertionSuccess(); +} + +TEST_F(FeatureOverrideParserTest, packageOverrides2) { + FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides(); + + EXPECT_TRUE(validatePackageOverrides2(overrides)); +} + +testing::AssertionResult validatePackageOverrides3(FeatureOverrides overrides) { + const std::string expectedPackageName = "com.gpuservice_unittest.packageOverrides3"; + + if (!overrides.mPackageFeatures.count(expectedPackageName)) { + return testing::AssertionFailure() + << "overrides.mPackageFeatures missing expected package: " << expectedPackageName; + } + + const std::vector<FeatureConfig>& features = overrides.mPackageFeatures[expectedPackageName]; + + size_t expectedFeaturesSize = 2; + if (features.size() != expectedFeaturesSize) { + return testing::AssertionFailure() + << "features.size(): " << features.size() + << ", expectedFeaturesSize: " << expectedFeaturesSize; + } + + std::string expectedFeatureName = "packageOverrides3_1"; + const FeatureConfig &cfg_1 = features[0]; + + if (cfg_1.mFeatureName != expectedFeatureName) { + return testing::AssertionFailure() + << "cfg.mFeatureName: " << cfg_1.mFeatureName + << ", expected: " << expectedFeatureName; + } + + bool expectedEnabled = false; + if (cfg_1.mEnabled != expectedEnabled) { + return testing::AssertionFailure() + << "cfg.mEnabled: " << cfg_1.mEnabled + << ", expected: " << expectedEnabled; + } + + std::vector<uint32_t> expectedGpuVendorIDs = { + 0, // GpuVendorID::VENDOR_ID_TEST + 0x13B5, // GpuVendorID::VENDOR_ID_ARM + }; + if (cfg_1.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) { + return testing::AssertionFailure() + << "cfg.mGpuVendorIDs.size(): " << cfg_1.mGpuVendorIDs.size() + << ", expected: " << expectedGpuVendorIDs.size(); + } + for (int i = 0; i < expectedGpuVendorIDs.size(); i++) { + if (cfg_1.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) { + std::stringstream msg; + msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg_1.mGpuVendorIDs[i] + << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i]; + return testing::AssertionFailure() << msg.str(); + } + } + + expectedFeatureName = "packageOverrides3_2"; + const FeatureConfig &cfg_2 = features[1]; + + if (cfg_2.mFeatureName != expectedFeatureName) { + return testing::AssertionFailure() + << "cfg.mFeatureName: " << cfg_2.mFeatureName + << ", expected: " << expectedFeatureName; + } + + expectedEnabled = true; + if (cfg_2.mEnabled != expectedEnabled) { + return testing::AssertionFailure() + << "cfg.mEnabled: " << cfg_2.mEnabled + << ", expected: " << expectedEnabled; + } + + expectedGpuVendorIDs = { + 0, // GpuVendorID::VENDOR_ID_TEST + 0x8086, // GpuVendorID::VENDOR_ID_INTEL + }; + if (cfg_2.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) { + return testing::AssertionFailure() + << "cfg.mGpuVendorIDs.size(): " << cfg_2.mGpuVendorIDs.size() + << ", expected: " << expectedGpuVendorIDs.size(); + } + for (int i = 0; i < expectedGpuVendorIDs.size(); i++) { + if (cfg_2.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) { + std::stringstream msg; + msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg_2.mGpuVendorIDs[i] + << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i]; + return testing::AssertionFailure() << msg.str(); + } + } + + return testing::AssertionSuccess(); +} + +TEST_F(FeatureOverrideParserTest, packageOverrides3) { +FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides(); + +EXPECT_TRUE(validatePackageOverrides3(overrides)); +} + TEST_F(FeatureOverrideParserTest, forceFileRead) { FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides(); diff --git a/services/gpuservice/tests/unittests/data/feature_config_test.txtpb b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb index 726779e332..44a6f78e84 100644 --- a/services/gpuservice/tests/unittests/data/feature_config_test.txtpb +++ b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb @@ -22,6 +22,22 @@ global_features [ { feature_name: "globalOverrides1" enabled: False + }, + { + feature_name: "globalOverrides2" + enabled: True + gpu_vendor_ids: [ + VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed. + VENDOR_ID_ARM + ] + }, + { + feature_name: "globalOverrides3" + enabled: True + gpu_vendor_ids: [ + VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed. + VENDOR_ID_INTEL + ] } ] @@ -36,5 +52,39 @@ package_features [ enabled: True } ] + }, + { + package_name: "com.gpuservice_unittest.packageOverrides2" + feature_configs: [ + { + feature_name: "packageOverrides2" + enabled: False + gpu_vendor_ids: [ + VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed. + VENDOR_ID_INTEL + ] + } + ] + }, + { + package_name: "com.gpuservice_unittest.packageOverrides3" + feature_configs: [ + { + feature_name: "packageOverrides3_1" + enabled: False + gpu_vendor_ids: [ + VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed. + VENDOR_ID_ARM + ] + }, + { + feature_name: "packageOverrides3_2" + enabled: True + gpu_vendor_ids: [ + VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed. + VENDOR_ID_INTEL + ] + } + ] } ] diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS index a63de1c306..13a089fc02 100644 --- a/services/gpuservice/vts/OWNERS +++ b/services/gpuservice/vts/OWNERS @@ -1,8 +1,5 @@ # Bug component: 653544 kocdemir@google.com paulthomson@google.com -pbaiget@google.com -lfy@google.com chrisforbes@google.com -lpy@google.com alecmouri@google.com diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 3140dc86e9..98f0f346df 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -19,9 +19,11 @@ #include <android-base/logging.h> #include <android/configuration.h> #include <com_android_input_flags.h> +#include <algorithm> #if defined(__ANDROID__) #include <gui/SurfaceComposerClient.h> #endif +#include <input/InputFlags.h> #include <input/Keyboard.h> #include <input/PrintTools.h> #include <unordered_set> @@ -29,42 +31,12 @@ #include "PointerChoreographer.h" #define INDENT " " +#define INDENT2 " " namespace android { namespace { -bool isFromMouse(const NotifyMotionArgs& args) { - return isFromSource(args.source, AINPUT_SOURCE_MOUSE) && - args.pointerProperties[0].toolType == ToolType::MOUSE; -} - -bool isFromTouchpad(const NotifyMotionArgs& args) { - return isFromSource(args.source, AINPUT_SOURCE_MOUSE) && - args.pointerProperties[0].toolType == ToolType::FINGER; -} - -bool isFromDrawingTablet(const NotifyMotionArgs& args) { - return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) && - isStylusToolType(args.pointerProperties[0].toolType); -} - -bool isHoverAction(int32_t action) { - return action == AMOTION_EVENT_ACTION_HOVER_ENTER || - action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT; -} - -bool isStylusHoverEvent(const NotifyMotionArgs& args) { - return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action); -} - -bool isMouseOrTouchpad(uint32_t sources) { - // Check if this is a mouse or touchpad, but not a drawing tablet. - return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) || - (isFromSource(sources, AINPUT_SOURCE_MOUSE) && - !isFromSource(sources, AINPUT_SOURCE_STYLUS)); -} - inline void notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId, vec2>> change, PointerChoreographerPolicyInterface& policy) { if (!change) { @@ -161,10 +133,11 @@ PointerChoreographer::PointerChoreographer( }), mNextListener(listener), mPolicy(policy), - mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT), + mCurrentMouseDisplayId(ui::LogicalDisplayId::INVALID), mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID), mShowTouchesEnabled(false), mStylusPointerIconEnabled(false), + mPointerMotionFilterEnabled(false), mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT), mIsWindowInfoListenerRegistered(false), mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)), @@ -236,15 +209,16 @@ NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& arg PointerDisplayChange pointerDisplayChange; { // acquire lock std::scoped_lock _l(getLock()); - if (isFromMouse(args)) { + if (isFromMouse(args.source, args.pointerProperties[0].toolType)) { newArgs = processMouseEventLocked(args); pointerDisplayChange = calculatePointerDisplayChangeToNotify(); - } else if (isFromTouchpad(args)) { + } else if (isFromTouchpad(args.source, args.pointerProperties[0].toolType)) { newArgs = processTouchpadEventLocked(args); pointerDisplayChange = calculatePointerDisplayChangeToNotify(); - } else if (isFromDrawingTablet(args)) { + } else if (isFromDrawingTablet(args.source, args.pointerProperties[0].toolType)) { processDrawingTabletEventLocked(args); - } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) { + } else if (mStylusPointerIconEnabled && + isStylusHoverEvent(args.source, args.pointerProperties, args.action)) { processStylusHoverEventLocked(args); } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) { processTouchscreenAndStylusEventLocked(args); @@ -322,9 +296,11 @@ void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArg PointerControllerInterface& pc) { const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - - vec2 unconsumedDelta = pc.move(deltaX, deltaY); - if (com::android::input::flags::connected_displays_cursor() && + vec2 filteredDelta = + filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY}, + newArgs.displayId); + vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y); + if (InputFlags::connectedDisplaysCursorEnabled() && (std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) { handleUnconsumedDeltaLocked(pc, unconsumedDelta); // pointer may have moved to a different viewport @@ -386,7 +362,7 @@ void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterfac LOG(FATAL) << "A cursor already exists on destination display" << destinationViewport.displayId; } - mDefaultMouseDisplayId = destinationViewport.displayId; + mCurrentMouseDisplayId = destinationViewport.displayId; auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId); pcNode.key() = destinationViewport.displayId; mMousePointersByDisplay.insert(std::move(pcNode)); @@ -501,6 +477,14 @@ void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& << args.dump(); } + // Fade the mouse pointer on the display if there is one when the stylus starts hovering. + if (args.action == AMOTION_EVENT_ACTION_HOVER_ENTER) { + if (const auto it = mMousePointersByDisplay.find(args.displayId); + it != mMousePointersByDisplay.end()) { + it->second->fade(PointerControllerInterface::Transition::GRADUAL); + } + } + // Get the stylus pointer controller for the device, or create one if it doesn't exist. auto [it, controllerAdded] = mStylusPointersByDevice.try_emplace(args.deviceId, @@ -552,9 +536,6 @@ void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) } void PointerChoreographer::onControllerAddedOrRemovedLocked() { - if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) { - return; - } bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() || !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty(); @@ -619,15 +600,21 @@ void PointerChoreographer::notifyPointerCaptureChanged( } void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) { - std::scoped_lock _l(getLock()); - mTopology = displayTopologyGraph; + PointerDisplayChange pointerDisplayChange; + { // acquire lock + std::scoped_lock _l(getLock()); + mTopology = displayTopologyGraph; + + // make primary display default mouse display, if it was not set or + // the existing display was removed + if (mCurrentMouseDisplayId == ui::LogicalDisplayId::INVALID || + mTopology.graph.find(mCurrentMouseDisplayId) == mTopology.graph.end()) { + mCurrentMouseDisplayId = mTopology.primaryDisplayId; + pointerDisplayChange = updatePointerControllersLocked(); + } + } // release lock - // make primary display default mouse display, if it was not set - // or the existing display was removed - if (mDefaultMouseDisplayId == ui::LogicalDisplayId::INVALID || - mTopology.graph.find(mDefaultMouseDisplayId) != mTopology.graph.end()) { - mDefaultMouseDisplayId = mTopology.primaryDisplayId; - } + notifyPointerDisplayChange(pointerDisplayChange, mPolicy); } void PointerChoreographer::dump(std::string& dump) { @@ -638,6 +625,8 @@ void PointerChoreographer::dump(std::string& dump) { mShowTouchesEnabled ? "true" : "false"); dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n", mStylusPointerIconEnabled ? "true" : "false"); + dump += StringPrintf(INDENT "Accessibility Pointer Motion Filter Enabled: %s\n", + mPointerMotionFilterEnabled ? "true" : "false"); dump += INDENT "MousePointerControllers:\n"; for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) { @@ -659,6 +648,8 @@ void PointerChoreographer::dump(std::string& dump) { std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT); dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump; } + dump += INDENT "DisplayTopologyGraph:\n"; + dump += addLinePrefix(mTopology.dump(), INDENT2); dump += "\n"; } @@ -674,7 +665,25 @@ const DisplayViewport* PointerChoreographer::findViewportByIdLocked( ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked( ui::LogicalDisplayId associatedDisplayId) const { - return associatedDisplayId.isValid() ? associatedDisplayId : mDefaultMouseDisplayId; + if (!InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled()) { + if (associatedDisplayId.isValid()) { + return associatedDisplayId; + } + return mCurrentMouseDisplayId.isValid() ? mCurrentMouseDisplayId + : ui::LogicalDisplayId::DEFAULT; + } + // Associated display is not included in the topology, return this associated display. + if (associatedDisplayId.isValid() && + mTopology.graph.find(associatedDisplayId) == mTopology.graph.end()) { + return associatedDisplayId; + } + if (mCurrentMouseDisplayId.isValid()) { + return mCurrentMouseDisplayId; + } + if (mTopology.primaryDisplayId.isValid()) { + return mTopology.primaryDisplayId; + } + return ui::LogicalDisplayId::DEFAULT; } std::pair<ui::LogicalDisplayId, PointerControllerInterface&> @@ -783,7 +792,8 @@ PointerChoreographer::PointerDisplayChange PointerChoreographer::calculatePointerDisplayChangeToNotify() { ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID; vec2 cursorPosition = {0, 0}; - if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId); + if (const auto it = + mMousePointersByDisplay.find(getTargetMouseDisplayLocked(mCurrentMouseDisplayId)); it != mMousePointersByDisplay.end()) { const auto& pointerController = it->second; // Use the displayId from the pointerController, because it accurately reflects whether @@ -800,12 +810,16 @@ PointerChoreographer::calculatePointerDisplayChangeToNotify() { } void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) { + if (InputFlags::connectedDisplaysCursorEnabled()) { + // In connected displays scenario, default mouse display will only be updated from topology. + return; + } PointerDisplayChange pointerDisplayChange; { // acquire lock std::scoped_lock _l(getLock()); - mDefaultMouseDisplayId = displayId; + mCurrentMouseDisplayId = displayId; pointerDisplayChange = updatePointerControllersLocked(); } // release lock @@ -973,6 +987,11 @@ void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) { mCurrentFocusedDisplay = displayId; } +void PointerChoreographer::setAccessibilityPointerMotionFilterEnabled(bool enabled) { + std::scoped_lock _l(getLock()); + mPointerMotionFilterEnabled = enabled; +} + PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor( ui::LogicalDisplayId displayId) { std::function<std::shared_ptr<PointerControllerInterface>()> ctor = @@ -1046,6 +1065,21 @@ PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId so return std::nullopt; } +vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked( + const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) { + if (!mPointerMotionFilterEnabled) { + return delta; + } + std::optional<vec2> filterResult = + mPolicy.filterPointerMotionForAccessibility(current, delta, displayId); + if (!filterResult.has_value()) { + // Disable filter when there's any error. + mPointerMotionFilterEnabled = false; + return delta; + } + return *filterResult; +} + // --- PointerChoreographer::PointerChoreographerDisplayInfoListener --- void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged( diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index a9d971abb4..67bdca1bab 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -90,6 +90,11 @@ public: * This method may be called on any thread (usually by the input manager on a binder thread). */ virtual void dump(std::string& dump) = 0; + + /** + * Enables motion event filter before pointer coordinates are determined. + */ + virtual void setAccessibilityPointerMotionFilterEnabled(bool enabled) = 0; }; class PointerChoreographer : public PointerChoreographerInterface { @@ -110,6 +115,7 @@ public: void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override; void setFocusedDisplay(ui::LogicalDisplayId displayId) override; void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph); + void setAccessibilityPointerMotionFilterEnabled(bool enabled) override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; @@ -168,6 +174,10 @@ private: const DisplayTopologyPosition sourceBoundary, int32_t sourceCursorOffsetPx) const REQUIRES(getLock()); + vec2 filterPointerMotionForAccessibilityLocked(const vec2& current, const vec2& delta, + const ui::LogicalDisplayId& displayId) + REQUIRES(getLock()); + /* Topology is initialized with default-constructed value, which is an empty topology. Till we * receive setDisplayTopology call. * Meanwhile Choreographer will treat every display as independent disconnected display. @@ -221,13 +231,19 @@ private: std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice GUARDED_BY(getLock()); - ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock()); + // In connected displays scenario, this tracks the latest display the cursor is at, within the + // DisplayTopology. By default, this will be set to topology primary display, and updated when + // mouse crossed to another display. + // In non-connected displays scenario, this will be treated as the default display cursor + // will be on, when mouse doesn't have associated display. + ui::LogicalDisplayId mCurrentMouseDisplayId GUARDED_BY(getLock()); ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock()); std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(getLock()); std::set<DeviceId> mMouseDevices GUARDED_BY(getLock()); std::vector<DisplayViewport> mViewports GUARDED_BY(getLock()); bool mShowTouchesEnabled GUARDED_BY(getLock()); bool mStylusPointerIconEnabled GUARDED_BY(getLock()); + bool mPointerMotionFilterEnabled GUARDED_BY(getLock()); std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden; ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock()); diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index becfb05f67..9cd76c7d11 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -125,10 +125,17 @@ struct KeyEntry : EventEntry { bool syntheticRepeat; // set to true for synthetic key repeats enum class InterceptKeyResult { + // The interception result is unknown. UNKNOWN, + // The event should be skipped and not sent to the application. SKIP, + // The event should be sent to the application. CONTINUE, + // The event should eventually be sent to the application, after a delay. TRY_AGAIN_LATER, + // The event should not be initially sent to the application, but instead go through + // post-processing to generate a fallback key event and then sent to the application. + FALLBACK, }; // These are special fields that may need to be modified while the event is being dispatched. mutable InterceptKeyResult interceptKeyResult; // set based on the interception result diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4c8147df2c..391703d506 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -32,6 +32,7 @@ #include <gui/SurfaceComposerClient.h> #endif #include <input/InputDevice.h> +#include <input/InputFlags.h> #include <input/PrintTools.h> #include <input/TraceTools.h> #include <openssl/mem.h> @@ -933,8 +934,10 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), + mWindowInfosVsyncId(-1), mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL), mConnectionManager(mLooper), + mTouchStates(mWindowInfos, mConnectionManager), mNextUnblockedEvent(nullptr), mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), @@ -949,7 +952,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, new LatencyAggregatorWithHistograms())) : std::move(std::unique_ptr<InputEventTimelineProcessor>( new LatencyAggregator()))), - mLatencyTracker(*mInputEventTimelineProcessor) { + mLatencyTracker(*mInputEventTimelineProcessor, mInputDevices) { mReporter = createInputReporter(); mWindowInfoListener = sp<DispatcherWindowListener>::make(*this); @@ -1465,14 +1468,13 @@ sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findTouchedWindowAt( std::vector<InputTarget> InputDispatcher::DispatcherTouchState::findOutsideTargets( ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow, - int32_t pointerId, const ConnectionManager& connections, - const DispatcherWindowInfo& windowInfos, std::function<void()> dump) { + int32_t pointerId, std::function<void()> dump) { if (touchedWindow == nullptr) { return {}; } // Traverse windows from front to back until we encounter the touched window. std::vector<InputTarget> outsideTargets; - const auto& windowHandles = windowInfos.getWindowHandlesForDisplay(displayId); + const auto& windowHandles = mWindowInfos.getWindowHandlesForDisplay(displayId); for (const sp<WindowInfoHandle>& windowHandle : windowHandles) { if (windowHandle == touchedWindow) { // Stop iterating once we found a touched window. Any WATCH_OUTSIDE_TOUCH window @@ -1484,13 +1486,10 @@ std::vector<InputTarget> InputDispatcher::DispatcherTouchState::findOutsideTarge if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) { std::bitset<MAX_POINTER_ID + 1> pointerIds; pointerIds.set(pointerId); - DispatcherTouchState::addPointerWindowTarget(windowHandle, - InputTarget::DispatchMode::OUTSIDE, - ftl::Flags<InputTarget::Flags>(), - pointerIds, - /*firstDownTimeInTarget=*/std::nullopt, - connections, windowInfos, dump, - outsideTargets); + addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::OUTSIDE, + ftl::Flags<InputTarget::Flags>(), pointerIds, + /*firstDownTimeInTarget=*/std::nullopt, + /*pointerDisplayId=*/std::nullopt, dump, outsideTargets); } } return outsideTargets; @@ -1962,11 +1961,74 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con } } + if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::FALLBACK) { + findAndDispatchFallbackEvent(currentTime, entry, inputTargets); + // Drop the key. + return true; + } + // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; } +void InputDispatcher::findAndDispatchFallbackEvent(nsecs_t currentTime, + std::shared_ptr<const KeyEntry> entry, + std::vector<InputTarget>& inputTargets) { + // Find the fallback associated with the incoming key event and dispatch it. + KeyEvent event = createKeyEvent(*entry); + const int32_t originalKeyCode = entry->keyCode; + + // Fetch the fallback event. + KeyCharacterMap::FallbackAction fallback; + for (const InputDeviceInfo& deviceInfo : mInputDevices) { + if (deviceInfo.getId() == entry->deviceId) { + const KeyCharacterMap* map = deviceInfo.getKeyCharacterMap(); + + LOG_ALWAYS_FATAL_IF(map == nullptr, "No KeyCharacterMap for device %d", + entry->deviceId); + map->getFallbackAction(entry->keyCode, entry->metaState, &fallback); + break; + } + } + + if (fallback.keyCode == AKEYCODE_UNKNOWN) { + // No fallback detected. + return; + } + + std::unique_ptr<KeyEntry> fallbackKeyEntry = + std::make_unique<KeyEntry>(mIdGenerator.nextId(), entry->injectionState, + event.getEventTime(), event.getDeviceId(), event.getSource(), + event.getDisplayId(), entry->policyFlags, entry->action, + event.getFlags() | AKEY_EVENT_FLAG_FALLBACK, + fallback.keyCode, event.getScanCode(), /*metaState=*/0, + event.getRepeatCount(), event.getDownTime()); + + if (mTracer) { + fallbackKeyEntry->traceTracker = + mTracer->traceDerivedEvent(*fallbackKeyEntry, *entry->traceTracker); + } + + for (const InputTarget& inputTarget : inputTargets) { + std::shared_ptr<Connection> connection = inputTarget.connection; + if (!connection->responsive || (connection->status != Connection::Status::NORMAL)) { + return; + } + + connection->inputState.setFallbackKey(originalKeyCode, fallback.keyCode); + if (entry->action == AKEY_EVENT_ACTION_UP) { + connection->inputState.removeFallbackKey(originalKeyCode); + } + + if (mTracer) { + mTracer->dispatchToTargetHint(*fallbackKeyEntry->traceTracker, inputTarget); + } + enqueueDispatchEntryAndStartDispatchCycleLocked(currentTime, connection, + std::move(fallbackKeyEntry), inputTarget); + } +} + void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) { LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS) << prefix << "eventTime=" << entry.eventTime << ", deviceId=" << entry.deviceId @@ -2052,8 +2114,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, Result<std::vector<InputTarget>, InputEventInjectionResult> result = mTouchStates - .findTouchedWindowTargets(currentTime, *entry, mConnectionManager, - mWindowInfos, + .findTouchedWindowTargets(currentTime, *entry, mDragState ? mDragState->dragWindow : nullptr, std::bind_front(&InputDispatcher:: addDragEventLocked, @@ -2373,8 +2434,7 @@ InputDispatcher::findFocusedWindowTargetLocked(nsecs_t currentTime, const EventE base::Result<std::vector<InputTarget>, os::InputEventInjectionResult> InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( - nsecs_t currentTime, const MotionEntry& entry, const ConnectionManager& connections, - const DispatcherWindowInfo& windowInfos, + nsecs_t currentTime, const MotionEntry& entry, const sp<android::gui::WindowInfoHandle> dragWindow, std::function<void(const MotionEntry&)> addDragEvent, std::function<void()> dump) { ATRACE_CALL(); @@ -2387,12 +2447,11 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( const int32_t maskedAction = MotionEvent::getActionMasked(action); // Copy current touch state into tempTouchState. - // This state will be used to update mTouchStatesByDisplay at the end of this function. + // This state will be used to update saved touch state at the end of this function. // If no state for the specified display exists, then our initial state will be empty. - const TouchState* oldState = nullptr; + const TouchState* oldState = getTouchStateForMotionEntry(entry); TouchState tempTouchState; - if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) { - oldState = &(it->second); + if (oldState != nullptr) { tempTouchState = *oldState; } @@ -2436,12 +2495,10 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( // be a pointer that would generate ACTION_DOWN, *and* touch should not already be down. const bool isStylus = isPointerFromStylus(entry, pointerIndex); sp<WindowInfoHandle> newTouchedWindowHandle = - windowInfos.findTouchedWindowAt(displayId, x, y, isStylus); + mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus); if (isDown) { - targets += DispatcherTouchState::findOutsideTargets(displayId, newTouchedWindowHandle, - pointer.id, connections, - windowInfos, dump); + targets += findOutsideTargets(displayId, newTouchedWindowHandle, pointer.id, dump); } LOG_IF(INFO, newTouchedWindowHandle == nullptr) << "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y) @@ -2454,7 +2511,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( } std::vector<sp<WindowInfoHandle>> newTouchedWindows = - findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, windowInfos); + findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, mWindowInfos); if (newTouchedWindowHandle != nullptr) { // Process the foreground window first so that it is the first to receive the event. newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle); @@ -2467,7 +2524,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( } for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) { - if (!canWindowReceiveMotion(windowHandle, entry, connections, windowInfos)) { + if (!canWindowReceiveMotion(windowHandle, entry)) { continue; } @@ -2479,8 +2536,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( // Set target flags. ftl::Flags<InputTarget::Flags> targetFlags = - DispatcherTouchState::getTargetFlags(windowHandle, {x, y}, isSplit, - windowInfos); + getTargetFlags(windowHandle, {x, y}, isSplit); // Update the temporary touch state. @@ -2510,7 +2566,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp<WindowInfoHandle> wallpaper = - windowInfos.findWallpaperWindowBelow(windowHandle); + mWindowInfos.findWallpaperWindowBelow(windowHandle); if (wallpaper != nullptr) { ftl::Flags<InputTarget::Flags> wallpaperFlags = InputTarget::Flags::WINDOW_IS_OBSCURED | @@ -2579,7 +2635,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( tempTouchState.getFirstForegroundWindowHandle(entry.deviceId); LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr); sp<WindowInfoHandle> newTouchedWindowHandle = - windowInfos.findTouchedWindowAt(displayId, x, y, isStylus); + mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus); // Verify targeted injection. if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) { @@ -2589,7 +2645,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( // Do not slide events to the window which can not receive motion event if (newTouchedWindowHandle != nullptr && - !canWindowReceiveMotion(newTouchedWindowHandle, entry, connections, windowInfos)) { + !canWindowReceiveMotion(newTouchedWindowHandle, entry)) { newTouchedWindowHandle = nullptr; } @@ -2606,18 +2662,16 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( const TouchedWindow& touchedWindow = tempTouchState.getTouchedWindow(oldTouchedWindowHandle); - DispatcherTouchState:: - addPointerWindowTarget(oldTouchedWindowHandle, - InputTarget::DispatchMode::SLIPPERY_EXIT, - ftl::Flags<InputTarget::Flags>(), pointerIds, - touchedWindow.getDownTimeInTarget(entry.deviceId), - connections, windowInfos, dump, targets); + addPointerWindowTarget(oldTouchedWindowHandle, + InputTarget::DispatchMode::SLIPPERY_EXIT, + ftl::Flags<InputTarget::Flags>(), pointerIds, + touchedWindow.getDownTimeInTarget(entry.deviceId), + /*pointerDisplayId=*/std::nullopt, dump, targets); // Make a slippery entrance into the new window. ftl::Flags<InputTarget::Flags> targetFlags = - DispatcherTouchState::getTargetFlags(newTouchedWindowHandle, {x, y}, - isSplit, windowInfos); + getTargetFlags(newTouchedWindowHandle, {x, y}, isSplit); tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, InputTarget::DispatchMode::SLIPPERY_ENTER, @@ -2625,10 +2679,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( entry.eventTime); // Check if the wallpaper window should deliver the corresponding event. - DispatcherTouchState::slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, - newTouchedWindowHandle, tempTouchState, - entry, targets, connections, windowInfos, - dump); + slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, + tempTouchState, entry, targets, dump); tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id, oldTouchedWindowHandle); } @@ -2660,12 +2712,10 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( std::bitset<MAX_POINTER_ID + 1> pointerIds; pointerIds.set(entry.pointerProperties[0].id); for (const TouchedWindow& touchedWindow : hoveringWindows) { - DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle, - touchedWindow.dispatchMode, - touchedWindow.targetFlags, pointerIds, - touchedWindow.getDownTimeInTarget( - entry.deviceId), - connections, windowInfos, dump, targets); + addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode, + touchedWindow.targetFlags, pointerIds, + touchedWindow.getDownTimeInTarget(entry.deviceId), + /*pointerDisplayId=*/std::nullopt, dump, targets); } } @@ -2694,7 +2744,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( for (InputTarget& target : targets) { if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) { sp<WindowInfoHandle> targetWindow = - windowInfos.findWindowHandle(target.connection->getToken()); + mWindowInfos.findWindowHandle(target.connection->getToken()); if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) { target.flags |= InputTarget::Flags::ZERO_COORDS; } @@ -2718,13 +2768,10 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( if (touchingPointers.empty()) { continue; } - DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle, - touchedWindow.dispatchMode, - touchedWindow.targetFlags, - getPointerIds(touchingPointers), - touchedWindow.getDownTimeInTarget( - entry.deviceId), - connections, windowInfos, dump, targets); + addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode, + touchedWindow.targetFlags, getPointerIds(touchingPointers), + touchedWindow.getDownTimeInTarget(entry.deviceId), + /*pointerDisplayId=*/displayId, dump, targets); } // During targeted injection, only allow owned targets to receive events @@ -2779,16 +2826,12 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { if (displayId >= ui::LogicalDisplayId::DEFAULT) { tempTouchState.clearWindowsWithoutPointers(); - mTouchStatesByDisplay[displayId] = tempTouchState; + saveTouchStateForMotionEntry(entry, std::move(tempTouchState)); } else { - mTouchStatesByDisplay.erase(displayId); + eraseTouchStateForMotionEntry(entry); } } - if (tempTouchState.windows.empty()) { - mTouchStatesByDisplay.erase(displayId); - } - return targets; } @@ -2811,8 +2854,9 @@ void InputDispatcher::finishDragAndDrop(ui::LogicalDisplayId displayId, float x, } void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { - if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId || - mDragState->deviceId != entry.deviceId) { + if (!mDragState || mDragState->deviceId != entry.deviceId || + !mWindowInfos.areDisplaysConnected(mDragState->dragWindow->getInfo()->displayId, + entry.displayId)) { return; } @@ -2931,8 +2975,8 @@ void InputDispatcher::DispatcherTouchState::addPointerWindowTarget( const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget, - const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos, - std::function<void()> dump, std::vector<InputTarget>& inputTargets) { + std::optional<ui::LogicalDisplayId> pointerDisplayId, std::function<void()> dump, + std::vector<InputTarget>& inputTargets) { if (pointerIds.none()) { for (const auto& target : inputTargets) { LOG(INFO) << "Target: " << target; @@ -2960,14 +3004,15 @@ void InputDispatcher::DispatcherTouchState::addPointerWindowTarget( const WindowInfo& windowInfo = *windowHandle->getInfo(); if (it == inputTargets.end()) { - std::shared_ptr<Connection> connection = connections.getConnection(windowInfo.token); + std::shared_ptr<Connection> connection = mConnectionManager.getConnection(windowInfo.token); if (connection == nullptr) { ALOGW("Not creating InputTarget for %s, no input channel", windowInfo.name.c_str()); return; } inputTargets.push_back( createInputTarget(connection, windowHandle, dispatchMode, targetFlags, - windowInfos.getRawTransform(*windowHandle->getInfo()), + mWindowInfos.getRawTransform(*windowHandle->getInfo(), + pointerDisplayId), firstDownTimeInTarget)); it = inputTargets.end() - 1; } @@ -4167,15 +4212,15 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0); mDragState.reset(); } - DispatcherTouchState:: - addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS, - ftl::Flags<InputTarget::Flags>(), pointerIds, - motionEntry.downTime, mConnectionManager, - mWindowInfos, - std::bind_front(&InputDispatcher:: - logDispatchStateLocked, - this), - targets); + mTouchStates + .addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS, + ftl::Flags<InputTarget::Flags>(), pointerIds, + motionEntry.downTime, + /*pointerDisplayId=*/std::nullopt, + std::bind_front(&InputDispatcher:: + logDispatchStateLocked, + this), + targets); } else { targets.emplace_back(fallbackTarget); // Since we don't have a window, use the display transform as the raw transform. @@ -4235,13 +4280,8 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( << "channel '" << connection->getInputChannelName() << "' ~ Synthesized " << downEvents.size() << " down events to ensure consistent event stream."; - auto touchedWindowHandleAndDisplay = - mTouchStates.findTouchedWindowHandleAndDisplay(connection->getToken()); - if (!touchedWindowHandleAndDisplay.has_value()) { - LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token"; - } - - const auto [windowHandle, displayId] = touchedWindowHandleAndDisplay.value(); + const auto [windowHandle, displayId] = + mTouchStates.findExistingTouchedWindowHandleAndDisplay(connection->getToken()); const bool wasEmpty = connection->outboundQueue.empty(); for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { @@ -4259,14 +4299,14 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( pointerIndex++) { pointerIds.set(motionEntry.pointerProperties[pointerIndex].id); } - DispatcherTouchState:: - addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS, - targetFlags, pointerIds, motionEntry.downTime, - mConnectionManager, mWindowInfos, - std::bind_front(&InputDispatcher:: - logDispatchStateLocked, - this), - targets); + mTouchStates + .addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS, + targetFlags, pointerIds, motionEntry.downTime, + /*pointerDisplayId=*/std::nullopt, + std::bind_front(&InputDispatcher:: + logDispatchStateLocked, + this), + targets); } else { targets.emplace_back(connection, targetFlags); // Since we don't have a window, use the display transform as the raw transform. @@ -4369,7 +4409,7 @@ void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedA std::scoped_lock _l(mLock); // Reset key repeating in case a keyboard device was added or removed or something. resetKeyRepeatLocked(); - mLatencyTracker.setInputDevices(args.inputDeviceInfos); + mInputDevices = args.inputDeviceInfos; } void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { @@ -5091,8 +5131,19 @@ sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandle( } // Only look through the requested display. - for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(*displayId)) { - if (windowHandle->getToken() == windowHandleToken) { + return findWindowHandleOnDisplay(windowHandleToken, *displayId); +} + +sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnConnectedDisplays( + const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const { + if (windowHandleToken == nullptr) { + return nullptr; + } + + sp<WindowInfoHandle> windowHandle; + for (ui::LogicalDisplayId connectedDisplayId : getConnectedDisplays(displayId)) { + windowHandle = findWindowHandleOnDisplay(windowHandleToken, connectedDisplayId); + if (windowHandle != nullptr) { return windowHandle; } } @@ -5174,13 +5225,38 @@ ui::Transform InputDispatcher::DispatcherWindowInfo::getDisplayTransform( } ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform( - const android::gui::WindowInfo& windowInfo) const { + const android::gui::WindowInfo& windowInfo, + std::optional<ui::LogicalDisplayId> pointerDisplayId) const { + // TODO(b/383092013): Handle TOPOLOGY_AWARE window flag. + // For now, we assume all windows are topology-aware and can handle cross-display streams. + if (com::android::input::flags::connected_displays_cursor() && pointerDisplayId.has_value() && + *pointerDisplayId != windowInfo.displayId) { + // Sending pointer to a different display than the window. This is a + // cross-display drag gesture, so always use the new display's transform. + return getDisplayTransform(*pointerDisplayId); + } // If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw" - // APIs. If not, fall back to using the DisplayInfo transform of the window's display. - return (input_flags::use_cloned_screen_coordinates_as_raw() && - windowInfo.cloneLayerStackTransform) - ? *windowInfo.cloneLayerStackTransform - : getDisplayTransform(windowInfo.displayId); + // APIs. If not, fall back to using the DisplayInfo transform of the window's display + bool useClonedScreenCoordinates = (input_flags::use_cloned_screen_coordinates_as_raw() && + windowInfo.cloneLayerStackTransform); + if (useClonedScreenCoordinates) { + return *windowInfo.cloneLayerStackTransform; + } + return getDisplayTransform(windowInfo.displayId); +} + +ui::LogicalDisplayId InputDispatcher::DispatcherWindowInfo::getPrimaryDisplayId( + ui::LogicalDisplayId displayId) const { + if (mTopology.graph.contains(displayId)) { + return mTopology.primaryDisplayId; + } + return displayId; +} + +bool InputDispatcher::DispatcherWindowInfo::areDisplaysConnected( + ui::LogicalDisplayId display1, ui::LogicalDisplayId display2) const { + return display1 == display2 || + (mTopology.graph.contains(display1) && mTopology.graph.contains(display2)); } std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const { @@ -5210,13 +5286,40 @@ std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() co } else { dump += "Displays: <none>\n"; } + dump += StringPrintf("mMaximumObscuringOpacityForTouch: %f\n", + mMaximumObscuringOpacityForTouch); + dump += "DisplayTopologyGraph:\n"; + dump += addLinePrefix(mTopology.dump(), INDENT); + dump += "\n"; return dump; } +std::vector<ui::LogicalDisplayId> InputDispatcher::DispatcherWindowInfo::getConnectedDisplays( + ui::LogicalDisplayId displayId) const { + if (!mTopology.graph.contains(displayId)) { + return {displayId}; + } + + std::vector<ui::LogicalDisplayId> connectedDisplays; + for (auto it : mTopology.graph) { + connectedDisplays.push_back(it.first); + } + return connectedDisplays; +} + +sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnDisplay( + const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const { + for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(displayId)) { + if (windowHandle->getToken() == windowHandleToken) { + return windowHandle; + } + } + return nullptr; +} + bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion( const sp<android::gui::WindowInfoHandle>& window, - const android::inputdispatcher::MotionEntry& motionEntry, - const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos) const { + const android::inputdispatcher::MotionEntry& motionEntry) const { const WindowInfo& info = *window->getInfo(); // Skip spy window targets that are not valid for targeted injection. @@ -5235,7 +5338,7 @@ bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion( return false; } - std::shared_ptr<Connection> connection = connections.getConnection(window->getToken()); + std::shared_ptr<Connection> connection = mConnectionManager.getConnection(window->getToken()); if (connection == nullptr) { ALOGW("Not sending touch to %s because there's no corresponding connection", window->getName().c_str()); @@ -5250,8 +5353,8 @@ bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion( // Drop events that can't be trusted due to occlusion const auto [x, y] = resolveTouchedPosition(motionEntry); DispatcherWindowInfo::TouchOcclusionInfo occlusionInfo = - windowInfos.computeTouchOcclusionInfo(window, x, y); - if (!windowInfos.isTouchTrusted(occlusionInfo)) { + mWindowInfos.computeTouchOcclusionInfo(window, x, y); + if (!mWindowInfos.isTouchTrusted(occlusionInfo)) { if (DEBUG_TOUCH_OCCLUSION) { ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y); for (const auto& log : occlusionInfo.debugInfo) { @@ -5264,7 +5367,7 @@ bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion( } // Drop touch events if requested by input feature - if (shouldDropInput(motionEntry, window, windowInfos)) { + if (shouldDropInput(motionEntry, window, mWindowInfos)) { return false; } @@ -5400,7 +5503,7 @@ void InputDispatcher::setInputWindowsLocked( CancelationOptions hoverCancellationOptions(CancelationOptions::Mode::CANCEL_HOVER_EVENTS, "WindowInfo changed", traceContext.getTracker()); const std::list<DispatcherTouchState::CancellationArgs> cancellations = - mTouchStates.updateFromWindowInfo(displayId, mWindowInfos); + mTouchStates.updateFromWindowInfo(displayId); for (const auto& cancellationArgs : cancellations) { switch (cancellationArgs.mode) { case CancelationOptions::Mode::CANCEL_POINTER_EVENTS: @@ -5441,26 +5544,25 @@ void InputDispatcher::setInputWindowsLocked( } std::list<InputDispatcher::DispatcherTouchState::CancellationArgs> -InputDispatcher::DispatcherTouchState::updateFromWindowInfo( - ui::LogicalDisplayId displayId, const DispatcherWindowInfo& windowInfos) { +InputDispatcher::DispatcherTouchState::updateFromWindowInfo(ui::LogicalDisplayId displayId) { std::list<CancellationArgs> cancellations; - if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) { - TouchState& state = it->second; - cancellations = eraseRemovedWindowsFromWindowInfo(state, displayId, windowInfos); + forTouchAndCursorStatesOnDisplay(displayId, [&](TouchState& state) { cancellations.splice(cancellations.end(), - updateHoveringStateFromWindowInfo(state, displayId, windowInfos)); - } + eraseRemovedWindowsFromWindowInfo(state, displayId)); + cancellations.splice(cancellations.end(), + updateHoveringStateFromWindowInfo(state, displayId)); + return false; + }); return cancellations; } std::list<InputDispatcher::DispatcherTouchState::CancellationArgs> InputDispatcher::DispatcherTouchState::eraseRemovedWindowsFromWindowInfo( - TouchState& state, ui::LogicalDisplayId displayId, - const DispatcherWindowInfo& windowInfos) { + TouchState& state, ui::LogicalDisplayId displayId) { std::list<CancellationArgs> cancellations; for (auto it = state.windows.begin(); it != state.windows.end();) { TouchedWindow& touchedWindow = *it; - if (windowInfos.isWindowPresent(touchedWindow.windowHandle)) { + if (mWindowInfos.isWindowPresent(touchedWindow.windowHandle)) { it++; continue; } @@ -5486,12 +5588,11 @@ InputDispatcher::DispatcherTouchState::eraseRemovedWindowsFromWindowInfo( std::list<InputDispatcher::DispatcherTouchState::CancellationArgs> InputDispatcher::DispatcherTouchState::updateHoveringStateFromWindowInfo( - TouchState& state, ui::LogicalDisplayId displayId, - const DispatcherWindowInfo& windowInfos) { + TouchState& state, ui::LogicalDisplayId displayId) { std::list<CancellationArgs> cancellations; // Check if the hovering should stop because the window is no longer eligible to receive it // (for example, if the touchable region changed) - ui::Transform displayTransform = windowInfos.getDisplayTransform(displayId); + ui::Transform displayTransform = mWindowInfos.getDisplayTransform(displayId); for (TouchedWindow& touchedWindow : state.windows) { std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf( [&](const PointerProperties& properties, float x, float y) { @@ -5746,8 +5847,7 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s "transferring touch from this window to another window", traceContext.getTracker()); - auto result = mTouchStates.transferTouchGesture(fromToken, toToken, mWindowInfos, - mConnectionManager); + auto result = mTouchStates.transferTouchGesture(fromToken, toToken); if (!result.has_value()) { return false; } @@ -5791,9 +5891,7 @@ std::optional<std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<Pointe std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>, std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>> InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IBinder>& fromToken, - const sp<android::IBinder>& toToken, - const DispatcherWindowInfo& windowInfos, - const ConnectionManager& connections) { + const sp<android::IBinder>& toToken) { // Find the target touch state and touched window by fromToken. auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(fromToken); if (!touchStateWindowAndDisplay.has_value()) { @@ -5811,7 +5909,10 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB const DeviceId deviceId = *deviceIds.begin(); const sp<WindowInfoHandle> fromWindowHandle = touchedWindow.windowHandle; - const sp<WindowInfoHandle> toWindowHandle = windowInfos.findWindowHandle(toToken, displayId); + // TouchState displayId may not be same as window displayId, we need to lookup for toToken on + // all connected displays. + const sp<WindowInfoHandle> toWindowHandle = + mWindowInfos.findWindowHandleOnConnectedDisplays(toToken, displayId); if (!toWindowHandle) { ALOGW("Cannot transfer touch because the transfer target window was not found."); return std::nullopt; @@ -5837,8 +5938,8 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB deviceId, pointers, downTimeInTarget); // Synthesize cancel for old window and down for new window. - std::shared_ptr<Connection> fromConnection = connections.getConnection(fromToken); - std::shared_ptr<Connection> toConnection = connections.getConnection(toToken); + std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken); + std::shared_ptr<Connection> toConnection = mConnectionManager.getConnection(toToken); std::list<CancellationArgs> cancellations; std::list<PointerDownArgs> pointerDowns; if (fromConnection != nullptr && toConnection != nullptr) { @@ -5849,7 +5950,7 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB // Check if the wallpaper window should deliver the corresponding event. auto [wallpaperCancellations, wallpaperPointerDowns] = transferWallpaperTouch(fromWindowHandle, toWindowHandle, state, deviceId, pointers, - oldTargetFlags, newTargetFlags, windowInfos, connections); + oldTargetFlags, newTargetFlags); cancellations.splice(cancellations.end(), wallpaperCancellations); pointerDowns.splice(pointerDowns.end(), wallpaperPointerDowns); @@ -5871,26 +5972,25 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB */ sp<WindowInfoHandle> InputDispatcher::DispatcherTouchState::findTouchedForegroundWindow( ui::LogicalDisplayId displayId) const { - const auto stateIt = mTouchStatesByDisplay.find(displayId); - if (stateIt == mTouchStatesByDisplay.end()) { - ALOGI("No touch state on display %s", displayId.toString().c_str()); - return nullptr; - } - - const TouchState& state = stateIt->second; sp<WindowInfoHandle> touchedForegroundWindow; - // If multiple foreground windows are touched, return nullptr - for (const TouchedWindow& window : state.windows) { - if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) { - if (touchedForegroundWindow != nullptr) { - ALOGI("Two or more foreground windows: %s and %s", - touchedForegroundWindow->getName().c_str(), - window.windowHandle->getName().c_str()); - return nullptr; + forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) { + // If multiple foreground windows are touched, return nullptr + for (const TouchedWindow& window : state.windows) { + if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) { + if (touchedForegroundWindow != nullptr) { + ALOGI("Two or more foreground windows: %s and %s", + touchedForegroundWindow->getName().c_str(), + window.windowHandle->getName().c_str()); + touchedForegroundWindow = nullptr; + return true; + } + touchedForegroundWindow = window.windowHandle; } - touchedForegroundWindow = window.windowHandle; } - } + return false; + }); + ALOGI_IF(touchedForegroundWindow == nullptr, + "No touch state or no touched foreground window on display %d", displayId.val()); return touchedForegroundWindow; } @@ -7013,11 +7113,6 @@ void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) setInputWindowsLocked(handles, displayId); } - if (update.vsyncId < mWindowInfosVsyncId) { - ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64 - ", current update vsync id: %" PRId64, - mWindowInfosVsyncId, update.vsyncId); - } mWindowInfosVsyncId = update.vsyncId; } // Wake up poll loop since it may need to make new input dispatching choices. @@ -7069,8 +7164,7 @@ void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanosecon void InputDispatcher::DispatcherTouchState::slipWallpaperTouch( ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle, const sp<WindowInfoHandle>& newWindowHandle, TouchState& state, const MotionEntry& entry, - std::vector<InputTarget>& targets, const ConnectionManager& connections, - const DispatcherWindowInfo& windowInfos, std::function<void()> dump) { + std::vector<InputTarget>& targets, std::function<void()> dump) { LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry; const DeviceId deviceId = entry.deviceId; const PointerProperties& pointerProperties = entry.pointerProperties[0]; @@ -7083,19 +7177,17 @@ void InputDispatcher::DispatcherTouchState::slipWallpaperTouch( const sp<WindowInfoHandle> oldWallpaper = oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr; const sp<WindowInfoHandle> newWallpaper = - newHasWallpaper ? windowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr; + newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr; if (oldWallpaper == newWallpaper) { return; } if (oldWallpaper != nullptr) { const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper); - DispatcherTouchState::addPointerWindowTarget(oldWallpaper, - InputTarget::DispatchMode::SLIPPERY_EXIT, - oldTouchedWindow.targetFlags, - getPointerIds(pointers), - oldTouchedWindow.getDownTimeInTarget(deviceId), - connections, windowInfos, dump, targets); + addPointerWindowTarget(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT, + oldTouchedWindow.targetFlags, getPointerIds(pointers), + oldTouchedWindow.getDownTimeInTarget(deviceId), + /*pointerDisplayId=*/std::nullopt, dump, targets); state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper); } @@ -7114,8 +7206,7 @@ InputDispatcher::DispatcherTouchState::transferWallpaperTouch( const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state, android::DeviceId deviceId, const std::vector<PointerProperties>& pointers, ftl::Flags<InputTarget::Flags> oldTargetFlags, - ftl::Flags<InputTarget::Flags> newTargetFlags, const DispatcherWindowInfo& windowInfos, - const ConnectionManager& connections) { + ftl::Flags<InputTarget::Flags> newTargetFlags) { const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) && fromWindowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); @@ -7126,7 +7217,7 @@ InputDispatcher::DispatcherTouchState::transferWallpaperTouch( const sp<WindowInfoHandle> oldWallpaper = oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr; const sp<WindowInfoHandle> newWallpaper = - newHasWallpaper ? windowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr; + newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr; if (oldWallpaper == newWallpaper) { return {}; } @@ -7147,10 +7238,10 @@ InputDispatcher::DispatcherTouchState::transferWallpaperTouch( state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, deviceId, pointers, downTimeInTarget); std::shared_ptr<Connection> wallpaperConnection = - connections.getConnection(newWallpaper->getToken()); + mConnectionManager.getConnection(newWallpaper->getToken()); if (wallpaperConnection != nullptr) { std::shared_ptr<Connection> toConnection = - connections.getConnection(toWindowHandle->getToken()); + mConnectionManager.getConnection(toWindowHandle->getToken()); toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState); pointerDowns.emplace_back(downTimeInTarget, wallpaperConnection, wallpaperFlags); } @@ -7350,9 +7441,12 @@ void InputDispatcher::DispatcherWindowInfo::setDisplayTopology( mTopology = displayTopologyGraph; } +InputDispatcher::DispatcherTouchState::DispatcherTouchState(const DispatcherWindowInfo& windowInfos, + const ConnectionManager& connections) + : mWindowInfos(windowInfos), mConnectionManager(connections) {} + ftl::Flags<InputTarget::Flags> InputDispatcher::DispatcherTouchState::getTargetFlags( - const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit, - const DispatcherWindowInfo& windowInfos) { + const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit) { ftl::Flags<InputTarget::Flags> targetFlags; if (canReceiveForegroundTouches(*targetWindow->getInfo())) { // There should only be one touched window that can be "foreground" for the pointer. @@ -7361,9 +7455,9 @@ ftl::Flags<InputTarget::Flags> InputDispatcher::DispatcherTouchState::getTargetF if (isSplit) { targetFlags |= InputTarget::Flags::SPLIT; } - if (windowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) { + if (mWindowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) { targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED; - } else if (windowInfos.isWindowObscured(targetWindow)) { + } else if (mWindowInfos.isWindowObscured(targetWindow)) { targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; } return targetFlags; @@ -7371,101 +7465,212 @@ ftl::Flags<InputTarget::Flags> InputDispatcher::DispatcherTouchState::getTargetF bool InputDispatcher::DispatcherTouchState::hasTouchingOrHoveringPointers( ui::LogicalDisplayId displayId, int32_t deviceId) const { - const auto touchStateIt = mTouchStatesByDisplay.find(displayId); - if (touchStateIt == mTouchStatesByDisplay.end()) { - return false; - } - return touchStateIt->second.hasTouchingPointers(deviceId) || - touchStateIt->second.hasHoveringPointers(deviceId); + bool hasTouchingOrHoveringPointers = false; + forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) { + hasTouchingOrHoveringPointers = + state.hasTouchingPointers(deviceId) || state.hasHoveringPointers(deviceId); + return hasTouchingOrHoveringPointers; + }); + return hasTouchingOrHoveringPointers; } bool InputDispatcher::DispatcherTouchState::isPointerInWindow(const sp<android::IBinder>& token, ui::LogicalDisplayId displayId, android::DeviceId deviceId, int32_t pointerId) const { - const auto touchStateIt = mTouchStatesByDisplay.find(displayId); - if (touchStateIt == mTouchStatesByDisplay.end()) { - return false; - } - for (const TouchedWindow& window : touchStateIt->second.windows) { - if (window.windowHandle->getToken() == token && - (window.hasTouchingPointer(deviceId, pointerId) || - window.hasHoveringPointer(deviceId, pointerId))) { - return true; + bool isPointerInWindow = false; + forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) { + for (const TouchedWindow& window : state.windows) { + if (window.windowHandle->getToken() == token && + (window.hasTouchingPointer(deviceId, pointerId) || + window.hasHoveringPointer(deviceId, pointerId))) { + isPointerInWindow = true; + return true; + } } - } - return false; + return false; + }); + return isPointerInWindow; } -std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>> -InputDispatcher::DispatcherTouchState::findTouchedWindowHandleAndDisplay( +std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId> +InputDispatcher::DispatcherTouchState::findExistingTouchedWindowHandleAndDisplay( const sp<android::IBinder>& token) const { - for (const auto& [displayId, state] : mTouchStatesByDisplay) { + std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>> + touchedWindowHandleAndDisplay; + forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, const TouchState& state) { for (const TouchedWindow& w : state.windows) { if (w.windowHandle->getToken() == token) { - return std::make_tuple(std::ref(w.windowHandle), displayId); + touchedWindowHandleAndDisplay.emplace(std::ref(w.windowHandle), displayId); + return true; } } - } - return std::nullopt; + return false; + }); + LOG_ALWAYS_FATAL_IF(!touchedWindowHandleAndDisplay.has_value(), + "%s : Touch state is out of sync: No touched window for token", __func__); + return touchedWindowHandleAndDisplay.value(); } void InputDispatcher::DispatcherTouchState::forAllTouchedWindows( std::function<void(const sp<gui::WindowInfoHandle>&)> f) const { - for (const auto& [_, state] : mTouchStatesByDisplay) { + forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, const TouchState& state) { for (const TouchedWindow& window : state.windows) { f(window.windowHandle); } - } + return false; + }); } void InputDispatcher::DispatcherTouchState::forAllTouchedWindowsOnDisplay( ui::LogicalDisplayId displayId, std::function<void(const sp<gui::WindowInfoHandle>&)> f) const { - const auto touchStateIt = mTouchStatesByDisplay.find(displayId); - if (touchStateIt == mTouchStatesByDisplay.end()) { - return; - } - for (const TouchedWindow& window : touchStateIt->second.windows) { - f(window.windowHandle); - } + forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) { + for (const TouchedWindow& window : state.windows) { + f(window.windowHandle); + } + return false; + }); } std::string InputDispatcher::DispatcherTouchState::dump() const { std::string dump; - if (!mTouchStatesByDisplay.empty()) { - dump += StringPrintf("TouchStatesByDisplay:\n"); + if (mTouchStatesByDisplay.empty()) { + dump += "TouchStatesByDisplay: <no displays touched>\n"; + } else { + dump += "TouchStatesByDisplay:\n"; for (const auto& [displayId, state] : mTouchStatesByDisplay) { std::string touchStateDump = addLinePrefix(state.dump(), INDENT); dump += INDENT + displayId.toString() + " : " + touchStateDump; } + } + if (mCursorStateByDisplay.empty()) { + dump += "CursorStatesByDisplay: <no displays touched by cursor>\n"; } else { - dump += "TouchStates: <no displays touched>\n"; + dump += "CursorStatesByDisplay:\n"; + for (const auto& [displayId, state] : mCursorStateByDisplay) { + std::string touchStateDump = addLinePrefix(state.dump(), INDENT); + dump += INDENT + displayId.toString() + " : " + touchStateDump; + } } return dump; } void InputDispatcher::DispatcherTouchState::removeAllPointersForDevice(android::DeviceId deviceId) { - for (auto& [_, touchState] : mTouchStatesByDisplay) { - touchState.removeAllPointersForDevice(deviceId); - } + forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, TouchState& state) { + state.removeAllPointersForDevice(deviceId); + return false; + }); } void InputDispatcher::DispatcherTouchState::clear() { mTouchStatesByDisplay.clear(); + mCursorStateByDisplay.clear(); +} + +void InputDispatcher::DispatcherTouchState::saveTouchStateForMotionEntry( + const android::inputdispatcher::MotionEntry& entry, + android::inputdispatcher::TouchState&& touchState) { + if (touchState.windows.empty()) { + eraseTouchStateForMotionEntry(entry); + return; + } + + if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) { + mCursorStateByDisplay[mWindowInfos.getPrimaryDisplayId(entry.displayId)] = + std::move(touchState); + } else { + mTouchStatesByDisplay[entry.displayId] = std::move(touchState); + } +} + +void InputDispatcher::DispatcherTouchState::eraseTouchStateForMotionEntry( + const android::inputdispatcher::MotionEntry& entry) { + if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) { + mCursorStateByDisplay.erase(mWindowInfos.getPrimaryDisplayId(entry.displayId)); + } else { + mTouchStatesByDisplay.erase(entry.displayId); + } +} + +const TouchState* InputDispatcher::DispatcherTouchState::getTouchStateForMotionEntry( + const android::inputdispatcher::MotionEntry& entry) const { + if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) { + auto touchStateIt = + mCursorStateByDisplay.find(mWindowInfos.getPrimaryDisplayId(entry.displayId)); + if (touchStateIt != mCursorStateByDisplay.end()) { + return &touchStateIt->second; + } + } else { + auto touchStateIt = mTouchStatesByDisplay.find(entry.displayId); + if (touchStateIt != mTouchStatesByDisplay.end()) { + return &touchStateIt->second; + } + } + return nullptr; +} + +void InputDispatcher::DispatcherTouchState::forTouchAndCursorStatesOnDisplay( + ui::LogicalDisplayId displayId, std::function<bool(const TouchState&)> f) const { + const auto touchStateIt = mTouchStatesByDisplay.find(displayId); + if (touchStateIt != mTouchStatesByDisplay.end() && f(touchStateIt->second)) { + return; + } + + // DisplayId for the Cursor state may not be same as supplied displayId if display is part of + // topology. Instead we should to check from the topology's primary display. + const auto cursorStateIt = + mCursorStateByDisplay.find(mWindowInfos.getPrimaryDisplayId(displayId)); + if (cursorStateIt != mCursorStateByDisplay.end()) { + f(cursorStateIt->second); + } +} + +void InputDispatcher::DispatcherTouchState::forTouchAndCursorStatesOnDisplay( + ui::LogicalDisplayId displayId, std::function<bool(TouchState&)> f) { + const_cast<const DispatcherTouchState&>(*this) + .forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) { + return f(const_cast<TouchState&>(state)); + }); +} + +void InputDispatcher::DispatcherTouchState::forAllTouchAndCursorStates( + std::function<bool(ui::LogicalDisplayId, const TouchState&)> f) const { + for (auto& [displayId, state] : mTouchStatesByDisplay) { + if (f(displayId, state)) { + return; + } + } + for (auto& [displayId, state] : mCursorStateByDisplay) { + if (f(displayId, state)) { + return; + } + } +} + +void InputDispatcher::DispatcherTouchState::forAllTouchAndCursorStates( + std::function<bool(ui::LogicalDisplayId, TouchState&)> f) { + const_cast<const DispatcherTouchState&>(*this).forAllTouchAndCursorStates( + [&](ui::LogicalDisplayId displayId, const TouchState& constState) { + return f(displayId, const_cast<TouchState&>(constState)); + }); } std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>> InputDispatcher::DispatcherTouchState::findTouchStateWindowAndDisplay( const sp<android::IBinder>& token) { - for (auto& [displayId, state] : mTouchStatesByDisplay) { + std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>> + touchStateWindowAndDisplay; + forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, TouchState& state) { for (TouchedWindow& w : state.windows) { if (w.windowHandle->getToken() == token) { - return std::make_tuple(std::ref(state), std::ref(w), displayId); + touchStateWindowAndDisplay.emplace(std::ref(state), std::ref(w), displayId); + return true; } } - } - return std::nullopt; + return false; + }); + return touchStateWindowAndDisplay; } bool InputDispatcher::DispatcherTouchState::isStylusActiveInDisplay( diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index e76bd8970f..ad7e87e192 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -307,8 +307,11 @@ private: // Get the transform for display, returns Identity-transform if display is missing. ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const; - // Get the raw transform to use for motion events going to the given window. - ui::Transform getRawTransform(const android::gui::WindowInfo&) const; + // Get the raw transform to use for motion events going to the given window. Optionally a + // pointer displayId may be supplied if pointer is on a different display from the window. + ui::Transform getRawTransform( + const android::gui::WindowInfo& windowInfo, + std::optional<ui::LogicalDisplayId> pointerDisplayId = std::nullopt) const; // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where // display-id is not provided lookup is done for all displays. @@ -316,6 +319,11 @@ private: const sp<IBinder>& windowHandleToken, std::optional<ui::LogicalDisplayId> displayId = {}) const; + // Lookup for WindowInfoHandle from token and a display-id. Lookup is done for all connected + // displays in the topology of the queried display. + sp<android::gui::WindowInfoHandle> findWindowHandleOnConnectedDisplays( + const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const; + bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const; // Returns the touched window at the given location, excluding the ignoreWindow if provided. @@ -336,9 +344,22 @@ private: bool isTouchTrusted(const TouchOcclusionInfo& occlusionInfo) const; + // Returns topology's primary display if the display belongs to it, otherwise the + // same displayId. + ui::LogicalDisplayId getPrimaryDisplayId(ui::LogicalDisplayId displayId) const; + + bool areDisplaysConnected(ui::LogicalDisplayId display1, + ui::LogicalDisplayId display2) const; + std::string dumpDisplayAndWindowInfo() const; private: + std::vector<ui::LogicalDisplayId> getConnectedDisplays( + ui::LogicalDisplayId displayId) const; + + sp<android::gui::WindowInfoHandle> findWindowHandleOnDisplay( + const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const; + std::unordered_map<ui::LogicalDisplayId /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>> mWindowHandlesByDisplay; @@ -370,20 +391,20 @@ private: const ftl::Flags<InputTarget::Flags> targetFlags; }; - static void addPointerWindowTarget(const sp<android::gui::WindowInfoHandle>& windowHandle, - InputTarget::DispatchMode dispatchMode, - ftl::Flags<InputTarget::Flags> targetFlags, - std::bitset<MAX_POINTER_ID + 1> pointerIds, - std::optional<nsecs_t> firstDownTimeInTarget, - const ConnectionManager& connections, - const DispatcherWindowInfo& windowInfos, - std::function<void()> dump, - std::vector<InputTarget>& inputTargets); + DispatcherTouchState(const DispatcherWindowInfo& windowInfos, + const ConnectionManager& connections); + + void addPointerWindowTarget(const sp<android::gui::WindowInfoHandle>& windowHandle, + InputTarget::DispatchMode dispatchMode, + ftl::Flags<InputTarget::Flags> targetFlags, + std::bitset<MAX_POINTER_ID + 1> pointerIds, + std::optional<nsecs_t> firstDownTimeInTarget, + std::optional<ui::LogicalDisplayId> pointerDisplayId, + std::function<void()> dump, + std::vector<InputTarget>& inputTargets); base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult> findTouchedWindowTargets(nsecs_t currentTime, const MotionEntry& entry, - const ConnectionManager& connections, - const DispatcherWindowInfo& windowInfos, const sp<android::gui::WindowInfoHandle> dragWindow, std::function<void(const MotionEntry&)> addDragEvent, std::function<void()> dump); @@ -396,9 +417,9 @@ private: bool isPointerInWindow(const sp<android::IBinder>& token, ui::LogicalDisplayId displayId, DeviceId deviceId, int32_t pointerId) const; - // Find touched windowHandle and display by token. - std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>> - findTouchedWindowHandleAndDisplay(const sp<IBinder>& token) const; + // Find an existing touched windowHandle and display by token. + std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId> + findExistingTouchedWindowHandleAndDisplay(const sp<IBinder>& token) const; void forAllTouchedWindows(std::function<void(const sp<gui::WindowInfoHandle>&)> f) const; @@ -410,8 +431,7 @@ private: // Updates the touchState for display from WindowInfo, // returns list of CancellationArgs for every cancelled touch - std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId, - const DispatcherWindowInfo& windowInfos); + std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId); void removeAllPointersForDevice(DeviceId deviceId); @@ -420,9 +440,7 @@ private: std::optional< std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<PointerProperties>, std::list<CancellationArgs>, std::list<PointerDownArgs>>> - transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - const DispatcherWindowInfo& windowInfos, - const ConnectionManager& connections); + transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); base::Result<std::list<CancellationArgs>, status_t> pilferPointers( const sp<IBinder>& token, const Connection& requestingConnection); @@ -432,6 +450,29 @@ private: private: std::unordered_map<ui::LogicalDisplayId, TouchState> mTouchStatesByDisplay; + // As there can be only one CursorState per topology group, we will treat all displays in + // the topology as one connected display-group. These will be identified by + // DisplayTopologyGraph::primaryDisplayId. + // Cursor on the any of the displays that are not part of the topology will be identified by + // the displayId similar to mTouchStatesByDisplay. + std::unordered_map<ui::LogicalDisplayId, TouchState> mCursorStateByDisplay; + + // The supplied lambda is invoked for each touch and cursor state of the display. + // The function iterates until the lambda returns true, effectively performing a 'break' + // from the iteration. + void forTouchAndCursorStatesOnDisplay(ui::LogicalDisplayId displayId, + std::function<bool(const TouchState&)> f) const; + + void forTouchAndCursorStatesOnDisplay(ui::LogicalDisplayId displayId, + std::function<bool(TouchState&)> f); + + // The supplied lambda is invoked for each touchState. The function iterates until + // the lambda returns true, effectively performing a 'break' from the iteration. + void forAllTouchAndCursorStates( + std::function<bool(ui::LogicalDisplayId, const TouchState&)> f) const; + + void forAllTouchAndCursorStates(std::function<bool(ui::LogicalDisplayId, TouchState&)> f); + std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>> findTouchStateWindowAndDisplay(const sp<IBinder>& token); @@ -440,30 +481,31 @@ private: const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state, DeviceId deviceId, const std::vector<PointerProperties>& pointers, ftl::Flags<InputTarget::Flags> oldTargetFlags, - ftl::Flags<InputTarget::Flags> newTargetFlags, - const DispatcherWindowInfo& windowInfos, const ConnectionManager& connections); + ftl::Flags<InputTarget::Flags> newTargetFlags); + + void saveTouchStateForMotionEntry(const MotionEntry& entry, TouchState&& touchState); - bool canWindowReceiveMotion(const sp<android::gui::WindowInfoHandle>& window, - const MotionEntry& motionEntry, - const ConnectionManager& connections, - const DispatcherWindowInfo& windowInfos) const; + void eraseTouchStateForMotionEntry(const MotionEntry& entry); + + const TouchState* getTouchStateForMotionEntry( + const android::inputdispatcher::MotionEntry& entry) const; + + bool canWindowReceiveMotion(const sp<gui::WindowInfoHandle>& window, + const MotionEntry& motionEntry) const; // Return true if stylus is currently down anywhere on the specified display, // and false otherwise. bool isStylusActiveInDisplay(ui::LogicalDisplayId displayId) const; - static std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo( - TouchState& state, ui::LogicalDisplayId displayId, - const DispatcherWindowInfo& windowInfos); + std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo( + TouchState& state, ui::LogicalDisplayId displayId); - static std::list<CancellationArgs> updateHoveringStateFromWindowInfo( - TouchState& state, ui::LogicalDisplayId displayId, - const DispatcherWindowInfo& windowInfos); + std::list<CancellationArgs> updateHoveringStateFromWindowInfo( + TouchState& state, ui::LogicalDisplayId displayId); - static std::vector<InputTarget> findOutsideTargets( - ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow, - int32_t pointerId, const ConnectionManager& connections, - const DispatcherWindowInfo& windowInfos, std::function<void()> dump); + std::vector<InputTarget> findOutsideTargets(ui::LogicalDisplayId displayId, + const sp<gui::WindowInfoHandle>& touchedWindow, + int32_t pointerId, std::function<void()> dump); /** * Slip the wallpaper touch if necessary. @@ -478,18 +520,18 @@ private: * @param targets the current targets to add the walpaper ones to * @param eventTime the new downTime for the wallpaper target */ - static void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, - const sp<android::gui::WindowInfoHandle>& oldWindowHandle, - const sp<android::gui::WindowInfoHandle>& newWindowHandle, - TouchState& state, const MotionEntry& entry, - std::vector<InputTarget>& targets, - const ConnectionManager& connections, - const DispatcherWindowInfo& windowInfos, - std::function<void()> dump); - - static ftl::Flags<InputTarget::Flags> getTargetFlags( + void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, + const sp<android::gui::WindowInfoHandle>& oldWindowHandle, + const sp<android::gui::WindowInfoHandle>& newWindowHandle, + TouchState& state, const MotionEntry& entry, + std::vector<InputTarget>& targets, std::function<void()> dump); + + ftl::Flags<InputTarget::Flags> getTargetFlags( const sp<android::gui::WindowInfoHandle>& targetWindow, vec2 targetPosition, - bool isSplit, const DispatcherWindowInfo& windowInfos); + bool isSplit); + + const DispatcherWindowInfo& mWindowInfos; + const ConnectionManager& mConnectionManager; }; DispatcherTouchState mTouchStates GUARDED_BY(mLock); @@ -876,10 +918,14 @@ private: std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable( const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) REQUIRES(mLock); + void findAndDispatchFallbackEvent(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, + std::vector<InputTarget>& inputTargets) REQUIRES(mLock); // Statistics gathering. nsecs_t mLastStatisticPushTime = 0; std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock); + // Must outlive `mLatencyTracker`. + std::vector<InputDeviceInfo> mInputDevices; LatencyTracker mLatencyTracker GUARDED_BY(mLock); void traceInboundQueueLengthLocked() REQUIRES(mLock); void traceOutboundQueueLength(const Connection& connection); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 9b5a79b24d..782a54f09e 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -15,7 +15,9 @@ */ #include "DebugConfig.h" +#include "input/Input.h" #include "input/InputDevice.h" +#include "input/InputFlags.h" #include "InputState.h" @@ -221,10 +223,15 @@ ssize_t InputState::findKeyMemento(const KeyEntry& entry) const { } ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const { + // If we have connected displays a mouse can move between displays and displayId may change + // while a gesture is in-progress. + const bool skipDisplayCheck = + InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source); for (size_t i = 0; i < mMotionMementos.size(); i++) { const MotionMemento& memento = mMotionMementos[i]; if (memento.deviceId == entry.deviceId && memento.source == entry.source && - memento.displayId == entry.displayId && memento.hovering == hovering) { + memento.hovering == hovering && + (skipDisplayCheck || memento.displayId == entry.displayId)) { return i; } } @@ -338,7 +345,10 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry) cons // would receive different events from each display. Since the TouchStates are per-display, // it's unlikely that those two streams would be consistent with each other. Therefore, // cancel the previous gesture if the display id changes. - if (motionEntry.displayId != lastMemento.displayId) { + // Except when we have connected-displays where a mouse may move across display boundaries. + const bool skipDisplayCheck = (InputFlags::connectedDisplaysCursorEnabled() && + isMouseOrTouchpad(motionEntry.source)); + if (!skipDisplayCheck && motionEntry.displayId != lastMemento.displayId) { LOG(INFO) << "Canceling stream: last displayId was " << lastMemento.displayId << " and new event is " << motionEntry; return true; diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index 0921e37d03..7c23694fbd 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -67,8 +67,9 @@ static void eraseByValue(std::multimap<K, V>& map, const V& value) { } // namespace -LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor) - : mTimelineProcessor(&processor) {} +LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor, + std::vector<InputDeviceInfo>& inputDevices) + : mTimelineProcessor(&processor), mInputDevices(inputDevices) {} void LatencyTracker::trackListener(const NotifyArgs& args) { if (const NotifyKeyArgs* keyArgs = std::get_if<NotifyKeyArgs>(&args)) { @@ -248,8 +249,4 @@ std::string LatencyTracker::dump(const char* prefix) const { StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size()); } -void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) { - mInputDevices = inputDevices; -} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 79ea14c4fb..7e2cc79249 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -20,9 +20,11 @@ #include <map> #include <unordered_map> +#include <vector> #include <binder/IBinder.h> #include <input/Input.h> +#include <input/InputDevice.h> #include "InputEventTimeline.h" #include "NotifyArgs.h" @@ -41,8 +43,10 @@ public: /** * Create a LatencyTracker. * param reportingFunction: the function that will be called in order to report full latency. + * param inputDevices: input devices relevant for tracking. */ - LatencyTracker(InputEventTimelineProcessor& processor); + LatencyTracker(InputEventTimelineProcessor& processor, + std::vector<InputDeviceInfo>& inputDevices); /** * Start keeping track of an event identified by the args. This must be called first. * If duplicate events are encountered (events that have the same eventId), none of them will be @@ -60,7 +64,6 @@ public: std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); std::string dump(const char* prefix) const; - void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices); private: /** @@ -81,7 +84,7 @@ private: std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes; InputEventTimelineProcessor* mTimelineProcessor; - std::vector<InputDeviceInfo> mInputDevices; + std::vector<InputDeviceInfo>& mInputDevices; void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction, diff --git a/services/inputflinger/docs/device_configuration.md b/services/inputflinger/docs/device_configuration.md new file mode 100644 index 0000000000..0b75eb28bd --- /dev/null +++ b/services/inputflinger/docs/device_configuration.md @@ -0,0 +1,10 @@ +# Input Device Configuration + +There are a number of properties that can be specified for an input device. + +|Property|Value| +|---|----| +|`audio.mic`|A boolean (`0` or `1`) that indicates whether the device has a microphone.| +|`device.additionalSysfsLedsNode`|A string representing the path to search for device lights to be used in addition to searching the device node itself for lights.| +|`device.internal`|A boolean (`0` or `1`) that indicates if this input device is part of the device as opposed to be externally attached.| +|`device.type`|A string representing if the device is of a certain type. Valid values include `rotaryEncoder` and `externalStylus`. diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h index 36614b2c95..c805b7459d 100644 --- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h +++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h @@ -61,6 +61,16 @@ public: /* Notifies that mouse cursor faded due to typing. */ virtual void notifyMouseCursorFadedOnTyping() = 0; + + /** + * Give accessibility a chance to filter motion event by pointer devices. + * The return values denotes the delta x and y after filtering it. + * + * This call happens on the input hot path and it is extremely performance sensitive. + * This also must not call back into native code. + */ + virtual std::optional<vec2> filterPointerMotionForAccessibility( + const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) = 0; }; } // namespace android diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 3934e783d1..dc7f7c1d26 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -66,9 +66,10 @@ filegroup { "mapper/accumulator/SingleTouchMotionAccumulator.cpp", "mapper/accumulator/TouchButtonAccumulator.cpp", "mapper/gestures/GestureConverter.cpp", - "mapper/gestures/GesturesLogging.cpp", + "mapper/gestures/GesturesLogcatAdapter.cpp", "mapper/gestures/HardwareProperties.cpp", "mapper/gestures/HardwareStateConverter.cpp", + "mapper/gestures/Logging.cpp", "mapper/gestures/PropertyProvider.cpp", "mapper/gestures/TimerProvider.cpp", ], diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 3c8b6f54c1..2fcb5d831f 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -351,6 +351,22 @@ static std::optional<std::array<LightColor, COLOR_NUM>> getColorIndexArray( return colors; } +static base::Result<std::shared_ptr<PropertyMap>> loadConfiguration( + const InputDeviceIdentifier& ident) { + std::string configurationFile = + getInputDeviceConfigurationFilePathByDeviceIdentifier(ident, + InputDeviceConfigurationFileType:: + CONFIGURATION); + if (configurationFile.empty()) { + ALOGD("No input device configuration file found for device '%s'.", ident.name.c_str()); + return base::Result<std::shared_ptr<PropertyMap>>(nullptr); + } + base::Result<std::shared_ptr<PropertyMap>> propertyMap = + PropertyMap::load(configurationFile.c_str()); + + return propertyMap; +} + /** * Read country code information exposed through the sysfs path and convert it to Layout info. */ @@ -409,11 +425,22 @@ static std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> readBatteryConf * Read information about lights exposed through the sysfs path. */ static std::unordered_map<int32_t /*lightId*/, RawLightInfo> readLightsConfiguration( - const std::filesystem::path& sysfsRootPath) { + const std::filesystem::path& sysfsRootPath, const std::shared_ptr<PropertyMap>& config) { std::unordered_map<int32_t, RawLightInfo> lightInfos; int32_t nextLightId = 0; - // Check if device has any lights. - const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS); + // Check if device has any lights. If the Input Device Configuration file specifies any lights, + // use those in addition to searching the device node itself for lights. + std::vector<std::filesystem::path> paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS); + + if (config) { + auto additionalLights = config->getString("device.additionalSysfsLedsNode"); + if (additionalLights) { + ALOGI("IDC specifies additional path for lights at '%s'", + additionalLights.value().c_str()); + paths.push_back(std::filesystem::path(additionalLights.value())); + } + } + for (const auto& nodePath : paths) { RawLightInfo info; info.id = ++nextLightId; @@ -532,17 +559,16 @@ std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisI // --- EventHub::Device --- EventHub::Device::Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier, - std::shared_ptr<const AssociatedDevice> assocDev) + std::shared_ptr<PropertyMap> config) : fd(fd), id(id), path(std::move(path)), identifier(std::move(identifier)), classes(0), - configuration(nullptr), + configuration(std::move(config)), virtualKeyMap(nullptr), ffEffectPlaying(false), ffEffectId(-1), - associatedDevice(std::move(assocDev)), controllerNumber(0), enabled(true), isVirtual(fd < 0), @@ -696,26 +722,6 @@ bool EventHub::Device::hasKeycodeInternalLocked(int keycode) const { return false; } -void EventHub::Device::loadConfigurationLocked() { - configurationFile = - getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier, - InputDeviceConfigurationFileType:: - CONFIGURATION); - if (configurationFile.empty()) { - ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str()); - } else { - android::base::Result<std::unique_ptr<PropertyMap>> propertyMap = - PropertyMap::load(configurationFile.c_str()); - if (!propertyMap.ok()) { - ALOGE("Error loading input device configuration file for device '%s'. " - "Using default configuration.", - identifier.name.c_str()); - } else { - configuration = std::move(*propertyMap); - } - } -} - bool EventHub::Device::loadVirtualKeyMapLocked() { // The virtual key map is supplied by the kernel as a system board property file. std::string propPath = "/sys/board_properties/virtualkeys."; @@ -1611,7 +1617,7 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { } std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDeviceLocked( - const std::filesystem::path& devicePath) const { + const std::filesystem::path& devicePath, const std::shared_ptr<PropertyMap>& config) const { const std::optional<std::filesystem::path> sysfsRootPathOpt = getSysfsRootPath(devicePath.c_str()); if (!sysfsRootPathOpt) { @@ -1628,8 +1634,13 @@ std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDevi if (!associatedDevice) { // Found matching associated device for the first time. associatedDevice = dev->associatedDevice; - // Reload this associated device if needed. - const auto reloadedDevice = AssociatedDevice(path); + // Reload this associated device if needed. Use the base device + // config. Note that this will essentially arbitrarily pick one + // Device as the base for the AssociatedDevice configuration. If + // there are multiple Device's that have a configuration for the + // AssociatedDevice, only one configuration will be chosen and will + // be used for all other AssociatedDevices for the same sysfs path. + const auto reloadedDevice = AssociatedDevice(path, associatedDevice->baseDevConfig); if (reloadedDevice != *dev->associatedDevice) { ALOGI("The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s", path.c_str(), associatedDevice->dump().c_str()); @@ -1642,16 +1653,18 @@ std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDevi if (!associatedDevice) { // No existing associated device found for this path, so create a new one. - associatedDevice = std::make_shared<AssociatedDevice>(path); + associatedDevice = std::make_shared<AssociatedDevice>(path, config); } return associatedDevice; } -EventHub::AssociatedDevice::AssociatedDevice(const std::filesystem::path& sysfsRootPath) +EventHub::AssociatedDevice::AssociatedDevice(const std::filesystem::path& sysfsRootPath, + std::shared_ptr<PropertyMap> config) : sysfsRootPath(sysfsRootPath), + baseDevConfig(std::move(config)), batteryInfos(readBatteryConfiguration(sysfsRootPath)), - lightInfos(readLightsConfiguration(sysfsRootPath)), + lightInfos(readLightsConfiguration(sysfsRootPath, baseDevConfig)), layoutInfo(readLayoutConfiguration(sysfsRootPath)) {} std::string EventHub::AssociatedDevice::dump() const { @@ -2337,11 +2350,21 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { // Fill in the descriptor. assignDescriptorLocked(identifier); + // Load the configuration file for the device. + std::shared_ptr<PropertyMap> configuration = nullptr; + base::Result<std::shared_ptr<PropertyMap>> propertyMapResult = loadConfiguration(identifier); + if (!propertyMapResult.ok()) { + ALOGE("Error loading input device configuration file for device '%s'. " + "Using default configuration. Error: %s", + identifier.name.c_str(), propertyMapResult.error().message().c_str()); + } else { + configuration = propertyMapResult.value(); + } + // Allocate device. (The device object takes ownership of the fd at this point.) int32_t deviceId = mNextDeviceId++; std::unique_ptr<Device> device = - std::make_unique<Device>(fd, deviceId, devicePath, identifier, - obtainAssociatedDeviceLocked(devicePath)); + std::make_unique<Device>(fd, deviceId, devicePath, identifier, configuration); ALOGV("add device %d: %s\n", deviceId, devicePath.c_str()); ALOGV(" bus: %04x\n" @@ -2356,8 +2379,8 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff); - // Load the configuration file for the device. - device->loadConfigurationLocked(); + // Obtain the associated device, if any. + device->associatedDevice = obtainAssociatedDeviceLocked(devicePath, device->configuration); // Figure out the kinds of events the device reports. device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask); @@ -2664,7 +2687,8 @@ void EventHub::sysfsNodeChanged(const std::string& sysfsNodePath) { testedDevices.emplace(dev.associatedDevice, false); return false; } - auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath); + auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath, + dev.associatedDevice->baseDevConfig); const bool changed = *dev.associatedDevice != reloadedDevice; testedDevices.emplace(dev.associatedDevice, changed); return changed; diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 7ab000bbd0..58df692b3e 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -32,6 +32,7 @@ #include <unistd.h> #include <utils/Errors.h> #include <utils/Thread.h> +#include <string> #include "InputDevice.h" #include "include/gestures.h" @@ -196,9 +197,8 @@ void InputReader::loopOnce() { if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (now >= mNextTimeout) { - if (debugRawEvents()) { - ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); - } + ALOGD_IF(debugRawEvents(), "Timeout expired, latency=%0.3fms", + (now - mNextTimeout) * 0.000001f); mNextTimeout = LLONG_MAX; mPendingArgs += timeoutExpiredLocked(now); } @@ -263,9 +263,7 @@ std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents } batchSize += 1; } - if (debugRawEvents()) { - ALOGD("BatchSize: %zu Count: %zu", batchSize, count); - } + ALOGD_IF(debugRawEvents(), "BatchSize: %zu Count: %zu", batchSize, count); out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize); } else { switch (rawEvent->type) { @@ -948,7 +946,10 @@ bool InputReader::setKernelWakeEnabled(int32_t deviceId, bool enabled) { void InputReader::dump(std::string& dump) { std::scoped_lock _l(mLock); + dumpLocked(dump); +} +void InputReader::dumpLocked(std::string& dump) { mEventHub->dump(dump); dump += "\n"; @@ -1035,6 +1036,12 @@ void InputReader::monitor() { InputReader::ContextImpl::ContextImpl(InputReader* reader) : mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {} +std::string InputReader::ContextImpl::dump() { + std::string dump; + mReader->dumpLocked(dump); + return dump; +} + void InputReader::ContextImpl::updateGlobalMetaState() { // lock is already held by the input loop mReader->updateGlobalMetaStateLocked(); diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index 7434ae4b0a..df22890319 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -78,10 +78,8 @@ std::optional<std::int32_t> PeripheralController::Light::getRawLightBrightness(i if (rawMaxBrightness != MAX_BRIGHTNESS) { brightness = brightness * ratio; } - if (DEBUG_LIGHT_DETAILS) { - ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId, - brightness, ratio); - } + ALOGD_IF(DEBUG_LIGHT_DETAILS, "getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", + rawLightId, brightness, ratio); return brightness; } @@ -97,10 +95,8 @@ void PeripheralController::Light::setRawLightBrightness(int32_t rawLightId, int3 if (rawMaxBrightness != MAX_BRIGHTNESS) { brightness = ceil(brightness / ratio); } - if (DEBUG_LIGHT_DETAILS) { - ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId, - brightness, ratio); - } + ALOGD_IF(DEBUG_LIGHT_DETAILS, "setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", + rawLightId, brightness, ratio); context.setLightBrightness(rawLightId, brightness); } @@ -453,10 +449,9 @@ void PeripheralController::configureLights() { if (rawInfo->flags.test(InputLightClass::GLOBAL)) { rawGlobalId = rawId; } - if (DEBUG_LIGHT_DETAILS) { - ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, rawInfo->name.c_str(), - rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), rawInfo->flags.string().c_str()); - } + ALOGD_IF(DEBUG_LIGHT_DETAILS, "Light rawId %d name %s max %d flags %s\n", rawInfo->id, + rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), + rawInfo->flags.string().c_str()); } // Construct a player ID light @@ -473,10 +468,8 @@ void PeripheralController::configureLights() { } // Construct a RGB light for composed RGB light if (hasRedLed && hasGreenLed && hasBlueLed) { - if (DEBUG_LIGHT_DETAILS) { - ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED), - rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE)); - } + ALOGD_IF(DEBUG_LIGHT_DETAILS, "Rgb light ids [%d, %d, %d]\n", rawRgbIds.at(LightColor::RED), + rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE)); bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) != keyboardBacklightIds.end() && keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) != @@ -518,9 +511,8 @@ void PeripheralController::configureLights() { // If the node is multi-color led, construct a MULTI_COLOR light if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) && rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) { - if (DEBUG_LIGHT_DETAILS) { - ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); - } + ALOGD_IF(DEBUG_LIGHT_DETAILS, "Multicolor light Id %d name %s\n", rawInfo.id, + rawInfo.name.c_str()); std::unique_ptr<Light> light = std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId, type, rawInfo.id); @@ -528,9 +520,8 @@ void PeripheralController::configureLights() { continue; } // Construct a Mono LED light - if (DEBUG_LIGHT_DETAILS) { - ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); - } + ALOGD_IF(DEBUG_LIGHT_DETAILS, "Mono light Id %d name %s\n", rawInfo.id, + rawInfo.name.c_str()); std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name, ++mNextId, type, rawInfo.id); @@ -552,10 +543,8 @@ bool PeripheralController::setLightColor(int32_t lightId, int32_t color) { return false; } auto& light = it->second; - if (DEBUG_LIGHT_DETAILS) { - ALOGD("setLightColor lightId %d type %s color 0x%x", lightId, - ftl::enum_string(light->type).c_str(), color); - } + ALOGD_IF(DEBUG_LIGHT_DETAILS, "setLightColor lightId %d type %s color 0x%x", lightId, + ftl::enum_string(light->type).c_str(), color); return light->setLightColor(color); } @@ -566,10 +555,8 @@ std::optional<int32_t> PeripheralController::getLightColor(int32_t lightId) { } auto& light = it->second; std::optional<int32_t> color = light->getLightColor(); - if (DEBUG_LIGHT_DETAILS) { - ALOGD("getLightColor lightId %d type %s color 0x%x", lightId, - ftl::enum_string(light->type).c_str(), color.value_or(0)); - } + ALOGD_IF(DEBUG_LIGHT_DETAILS, "getLightColor lightId %d type %s color 0x%x", lightId, + ftl::enum_string(light->type).c_str(), color.value_or(0)); return color; } diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 31ac63f31e..adbfdebfb0 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -21,6 +21,7 @@ #include <filesystem> #include <functional> #include <map> +#include <memory> #include <optional> #include <ostream> #include <string> @@ -87,6 +88,7 @@ std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisI * If any new classes are added, we need to add them in rust input side too. */ enum class InputDeviceClass : uint32_t { + // LINT.IfChange /* The input device is a keyboard or has buttons. */ KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD, @@ -143,6 +145,7 @@ enum class InputDeviceClass : uint32_t { /* The input device is external (not built-in). */ EXTERNAL = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL, + // LINT.ThenChange(frameworks/native/services/inputflinger/tests/fuzzers/MapperHelpers.h) }; enum class SysfsClass : uint32_t { @@ -619,9 +622,12 @@ public: private: // Holds information about the sysfs device associated with the Device. struct AssociatedDevice { - AssociatedDevice(const std::filesystem::path& sysfsRootPath); + AssociatedDevice(const std::filesystem::path& sysfsRootPath, + std::shared_ptr<PropertyMap> baseDevConfig); // The sysfs root path of the misc device. std::filesystem::path sysfsRootPath; + // The configuration of the base device. + std::shared_ptr<PropertyMap> baseDevConfig; std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos; std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos; std::optional<RawLayoutInfo> layoutInfo; @@ -658,7 +664,7 @@ private: std::map<int /*axis*/, AxisState> absState; std::string configurationFile; - std::unique_ptr<PropertyMap> configuration; + std::shared_ptr<PropertyMap> configuration; std::unique_ptr<VirtualKeyMap> virtualKeyMap; KeyMap keyMap; @@ -672,7 +678,7 @@ private: int32_t controllerNumber; Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier, - std::shared_ptr<const AssociatedDevice> assocDev); + std::shared_ptr<PropertyMap> config); ~Device(); void close(); @@ -692,7 +698,6 @@ private: void populateAbsoluteAxisStates(); bool hasKeycodeLocked(int keycode) const; bool hasKeycodeInternalLocked(int keycode) const; - void loadConfigurationLocked(); bool loadVirtualKeyMapLocked(); status_t loadKeyMapLocked(); bool isExternalDeviceLocked(); @@ -724,7 +729,8 @@ private: void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock); void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock); std::shared_ptr<const AssociatedDevice> obtainAssociatedDeviceLocked( - const std::filesystem::path& devicePath) const REQUIRES(mLock); + const std::filesystem::path& devicePath, + const std::shared_ptr<PropertyMap>& config) const REQUIRES(mLock); void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock); void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 0d6e1020c1..6a259373df 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -21,6 +21,7 @@ #include <utils/Mutex.h> #include <memory> +#include <string> #include <unordered_map> #include <vector> @@ -142,6 +143,7 @@ protected: public: explicit ContextImpl(InputReader* reader); + std::string dump() REQUIRES(mReader->mLock) override; // lock is already held by the input loop void updateGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override; int32_t getGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override; @@ -216,6 +218,8 @@ private: // The input device that produced a new gesture most recently. DeviceId mLastUsedDeviceId GUARDED_BY(mLock){ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID}; + void dumpLocked(std::string& dump) REQUIRES(mLock); + // low-level input event decoding and device management [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index 20ed74fef7..f38fd7b8ac 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -20,6 +20,7 @@ #include <input/KeyboardClassifier.h> #include "NotifyArgs.h" +#include <string> #include <vector> namespace android { @@ -39,6 +40,8 @@ public: InputReaderContext() {} virtual ~InputReaderContext() {} + virtual std::string dump() = 0; + virtual void updateGlobalMetaState() = 0; virtual int32_t getGlobalMetaState() = 0; diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp index dd46bbc543..d796af158b 100644 --- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp +++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp @@ -25,8 +25,6 @@ #include <linux/input-event-codes.h> #include <log/log_main.h> -namespace input_flags = com::android::input::flags; - namespace android { namespace { @@ -119,15 +117,10 @@ std::string CapturedTouchpadEventConverter::dump() const { } void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) const { - if (input_flags::include_relative_axis_values_for_captured_touchpads()) { - tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X, - AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X); - tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y, - AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y); - } else { - tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X); - tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y); - } + tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X, + AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X); + tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y, + AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y); tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR); tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MINOR, ABS_MT_TOUCH_MINOR); tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR); @@ -213,13 +206,11 @@ std::list<NotifyArgs> CapturedTouchpadEventConverter::sync(nsecs_t when, nsecs_t } out.push_back( makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, coords, properties)); - if (input_flags::include_relative_axis_values_for_captured_touchpads()) { - // For any further events we send from this sync, the pointers won't have moved relative - // to the positions we just reported in this MOVE event, so zero out the relative axes. - for (PointerCoords& pointer : coords) { - pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); - pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); - } + // For any further events we send from this sync, the pointers won't have moved relative to + // the positions we just reported in this MOVE event, so zero out the relative axes. + for (PointerCoords& pointer : coords) { + pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); + pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); } } @@ -275,9 +266,7 @@ std::list<NotifyArgs> CapturedTouchpadEventConverter::sync(nsecs_t when, nsecs_t /*flags=*/cancel ? AMOTION_EVENT_FLAG_CANCELED : 0)); freePointerIdForSlot(slotNumber); - if (input_flags::include_relative_axis_values_for_captured_touchpads()) { - mPreviousCoordsForSlotNumber.erase(slotNumber); - } + mPreviousCoordsForSlotNumber.erase(slotNumber); coords.erase(coords.begin() + indexToRemove); properties.erase(properties.begin() + indexToRemove); // Now that we've removed some coords and properties, we might have to update the slot @@ -336,15 +325,13 @@ PointerCoords CapturedTouchpadEventConverter::makePointerCoordsForSlot(size_t sl coords.clear(); coords.setAxisValue(AMOTION_EVENT_AXIS_X, slot.getX()); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, slot.getY()); - if (input_flags::include_relative_axis_values_for_captured_touchpads()) { - if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber); - it != mPreviousCoordsForSlotNumber.end()) { - auto [oldX, oldY] = it->second; - coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX); - coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY); - } - mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY()); + if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber); + it != mPreviousCoordsForSlotNumber.end()) { + auto [oldX, oldY] = it->second; + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY); } + mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY()); coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, slot.getTouchMajor()); coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, slot.getTouchMinor()); diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index fd8224a608..4d08f1965e 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -79,19 +79,17 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { if (id) { outState->rawPointerData.canceledIdBits.markBit(id.value()); } - if (DEBUG_POINTERS) { - ALOGI("Stop processing slot %zu for it received a palm event from device %s", - inIndex, getDeviceName().c_str()); - } + ALOGI_IF(DEBUG_POINTERS, + "Stop processing slot %zu for it received a palm event from device %s", + inIndex, getDeviceName().c_str()); continue; } if (outCount >= MAX_POINTERS) { - if (DEBUG_POINTERS) { - ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; " - "ignoring the rest.", - getDeviceName().c_str(), MAX_POINTERS); - } + ALOGD_IF(DEBUG_POINTERS, + "MultiTouch device %s emitted more than maximum of %zu pointers; ignoring the " + "rest.", + getDeviceName().c_str(), MAX_POINTERS); break; // too many fingers! } diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index 1f6600d3f6..0d1d8844c1 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -235,9 +235,8 @@ void SensorInputMapper::processHardWareTimestamp(nsecs_t evTime, int32_t mscTime // else calculate difference between previous and current MSC_TIMESTAMP if (mPrevMscTime == 0) { mHardwareTimestamp = evTime; - if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Initialize hardware timestamp = %" PRId64, mHardwareTimestamp); - } + ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Initialize hardware timestamp = %" PRId64, + mHardwareTimestamp); } else { // Calculate the difference between current msc_timestamp and // previous msc_timestamp, including when msc_timestamp wraps around. @@ -330,11 +329,10 @@ void SensorInputMapper::flushSensor(InputDeviceSensorType sensorType) { bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod, std::chrono::microseconds maxBatchReportLatency) { - if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld", - ftl::enum_string(sensorType).c_str(), samplingPeriod.count(), - maxBatchReportLatency.count()); - } + ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, + "Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld", + ftl::enum_string(sensorType).c_str(), samplingPeriod.count(), + maxBatchReportLatency.count()); if (!setSensorEnabled(sensorType, /*enabled=*/true)) { return false; @@ -355,9 +353,7 @@ bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType, } void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) { - if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str()); - } + ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Disable Sensor %s", ftl::enum_string(sensorType).c_str()); if (!setSensorEnabled(sensorType, /*enabled=*/false)) { return; @@ -389,15 +385,12 @@ std::list<NotifyArgs> SensorInputMapper::sync(nsecs_t when, bool force) { } nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when; - if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]", - ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]); - } + ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Sensor %s timestamp %" PRIu64 " values [%f %f %f]", + ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]); if (sensor.lastSampleTimeNs.has_value() && timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) { - if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str()); - } + ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Sensor %s Skip a sample.", + ftl::enum_string(sensorType).c_str()); } else { // Convert to Android unit convertFromLinuxToAndroid(values, sensorType); diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp index 9ec02a6f86..d55df511e1 100644 --- a/services/inputflinger/reader/mapper/SlopController.cpp +++ b/services/inputflinger/reader/mapper/SlopController.cpp @@ -54,13 +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); + 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); + ALOGD("SlopController: dropping event with value %.3f", value); return 0; } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 8deff6b3aa..4d36db8ff7 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -24,6 +24,8 @@ #include <cinttypes> #include <cmath> #include <cstddef> +#include <sstream> +#include <string> #include <tuple> #include <math.h> @@ -138,6 +140,40 @@ void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) con *outY = y; } +std::ostream& operator<<(std::ostream& out, const RawPointerData::Pointer& p) { + out << "id=" << p.id << ", x=" << p.x << ", y=" << p.y << ", pressure=" << p.pressure + << ", touchMajor=" << p.touchMajor << ", touchMinor=" << p.touchMinor + << ", toolMajor=" << p.toolMajor << ", toolMinor=" << p.toolMinor + << ", orientation=" << p.orientation << ", tiltX=" << p.tiltX << ", tiltY=" << p.tiltY + << ", distance=" << p.distance << ", toolType=" << ftl::enum_string(p.toolType) + << ", isHovering=" << p.isHovering; + return out; +} + +std::ostream& operator<<(std::ostream& out, const RawPointerData& data) { + out << data.pointerCount << " pointers:\n"; + for (uint32_t i = 0; i < data.pointerCount; i++) { + out << INDENT << "[" << i << "]: " << data.pointers[i] << std::endl; + } + out << "ID bits: hovering = 0x" << std::hex << std::setfill('0') << std::setw(8) + << data.hoveringIdBits.value << ", touching = 0x" << std::setfill('0') << std::setw(8) + << data.touchingIdBits.value << ", canceled = 0x" << std::setfill('0') << std::setw(8) + << data.canceledIdBits.value << std::dec; + return out; +} + +// --- TouchInputMapper::RawState --- + +std::ostream& operator<<(std::ostream& out, const TouchInputMapper::RawState& state) { + out << "When: " << state.when << std::endl; + out << "Read time: " << state.readTime << std::endl; + out << "Button state: 0x" << std::setfill('0') << std::setw(8) << std::hex << state.buttonState + << std::dec << std::endl; + out << "Raw pointer data:" << std::endl; + out << addLinePrefix(streamableToString(state.rawPointerData), INDENT); + return out; +} + // --- TouchInputMapper --- TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext, @@ -232,20 +268,8 @@ void TouchInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); - dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", - mLastRawState.rawPointerData.pointerCount); - for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { - const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " - "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " - "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " - "toolType=%s, isHovering=%s\n", - i, pointer.id, pointer.x, pointer.y, pointer.pressure, - pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, - pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY, - pointer.distance, ftl::enum_string(pointer.toolType).c_str(), - toString(pointer.isHovering)); - } + dump += INDENT3 "Last Raw Touch:\n"; + dump += addLinePrefix(streamableToString(mLastRawState), INDENT4) + "\n"; dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", mLastCookedState.buttonState); @@ -1476,6 +1500,22 @@ std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value, last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value, next.rawPointerData.canceledIdBits.value); + if (debugRawEvents() && last.rawPointerData.pointerCount == 0 && + next.rawPointerData.pointerCount == 1) { + // Dump a bunch of info to try to debug b/396796958. + // TODO(b/396796958): remove this debug dump. + ALOGD("pointerCount went from 0 to 1. last:\n%s", + addLinePrefix(streamableToString(last), INDENT).c_str()); + ALOGD("next:\n%s", addLinePrefix(streamableToString(next), INDENT).c_str()); + ALOGD("InputReader dump:"); + // The dump is too long to simply add as a format parameter in one log message, so we have + // to split it by line and log them individually. + std::istringstream stream(mDeviceContext.getContext()->dump()); + std::string line; + while (std::getline(stream, line, '\n')) { + ALOGD(INDENT "%s", line.c_str()); + } + } if (!next.rawPointerData.touchingIdBits.isEmpty() && !next.rawPointerData.hoveringIdBits.isEmpty() && diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 4ef0be8f8e..45fc6bfa36 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -348,6 +348,8 @@ protected: inline void clear() { *this = RawState(); } }; + friend std::ostream& operator<<(std::ostream& out, const RawState& state); + struct CookedState { // Cooked pointer sample data. CookedPointerData cookedPointerData{}; diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 18a7102514..63eb357bdb 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -40,6 +40,7 @@ #include "TouchCursorInputMapperCommon.h" #include "TouchpadInputMapper.h" #include "gestures/HardwareProperties.h" +#include "gestures/Logging.h" #include "gestures/TimerProvider.h" #include "ui/Rotation.h" @@ -49,15 +50,6 @@ namespace android { namespace { -/** - * Log details of each gesture output by the gestures library. - * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires - * restarting the shell) - */ -const bool DEBUG_TOUCHPAD_GESTURES = - __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures", - ANDROID_LOG_INFO); - std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity, bool accelerationEnabled, size_t propertySize) { @@ -152,28 +144,39 @@ public: // records it if so. void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) { std::scoped_lock lock(mLock); + Counters& counters = mCounters[id]; switch (gesture.type) { case kGestureTypeFling: if (gesture.details.fling.fling_state == GESTURES_FLING_START) { // Indicates the end of a two-finger scroll gesture. - mCounters[id].twoFingerSwipeGestures++; + counters.twoFingerSwipeGestures++; } break; case kGestureTypeSwipeLift: - mCounters[id].threeFingerSwipeGestures++; + // The Gestures library occasionally outputs two lift gestures in a row, which can + // cause inaccurate metrics reporting. To work around this, deduplicate successive + // lift gestures. + // TODO(b/404529050): fix the Gestures library, and remove this check. + if (counters.lastGestureType != kGestureTypeSwipeLift) { + counters.threeFingerSwipeGestures++; + } break; case kGestureTypeFourFingerSwipeLift: - mCounters[id].fourFingerSwipeGestures++; + // TODO(b/404529050): fix the Gestures library, and remove this check. + if (counters.lastGestureType != kGestureTypeFourFingerSwipeLift) { + counters.fourFingerSwipeGestures++; + } break; case kGestureTypePinch: if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) { - mCounters[id].pinchGestures++; + counters.pinchGestures++; } break; default: // We're not interested in any other gestures. break; } + counters.lastGestureType = gesture.type; } private: @@ -222,6 +225,10 @@ private: int32_t threeFingerSwipeGestures = 0; int32_t fourFingerSwipeGestures = 0; int32_t pinchGestures = 0; + + // Records the last type of gesture received for this device, for deduplication purposes. + // TODO(b/404529050): fix the Gestures library and remove this field. + GestureType lastGestureType = kGestureTypeContactInitiated; }; // Metrics are aggregated by device model and version, so if two devices of the same model and @@ -470,7 +477,7 @@ void TouchpadInputMapper::updatePalmDetectionMetrics() { std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime, SelfContainedHardwareState schs) { - ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str()); + ALOGD_IF(debugTouchpadGestures(), "New hardware state: %s", schs.state.String().c_str()); mGestureInterpreter->PushHardwareState(&schs.state); return processGestures(when, readTime); } @@ -481,7 +488,7 @@ std::list<NotifyArgs> TouchpadInputMapper::timeoutExpired(nsecs_t when) { } void TouchpadInputMapper::consumeGesture(const Gesture* gesture) { - ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str()); + ALOGD_IF(debugTouchpadGestures(), "Gesture ready: %s", gesture->String().c_str()); if (mResettingInterpreter) { // We already handle tidying up fake fingers etc. in GestureConverter::reset, so we should // ignore any gestures produced from the interpreter while we're resetting it. diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index a3a48ef034..264ef6f3e0 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -43,10 +43,8 @@ std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent& rawEvent) { std::list<NotifyArgs> VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) { - if (DEBUG_VIBRATOR) { - ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), - sequence.toString().c_str(), repeat, token); - } + ALOGD_IF(DEBUG_VIBRATOR, "vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", + getDeviceId(), sequence.toString().c_str(), repeat, token); std::list<NotifyArgs> out; mVibrating = true; @@ -63,9 +61,7 @@ std::list<NotifyArgs> VibratorInputMapper::vibrate(const VibrationSequence& sequ } std::list<NotifyArgs> VibratorInputMapper::cancelVibrate(int32_t token) { - if (DEBUG_VIBRATOR) { - ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); - } + ALOGD_IF(DEBUG_VIBRATOR, "cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); std::list<NotifyArgs> out; if (mVibrating && mToken == token) { @@ -95,9 +91,7 @@ std::list<NotifyArgs> VibratorInputMapper::timeoutExpired(nsecs_t when) { } std::list<NotifyArgs> VibratorInputMapper::nextStep() { - if (DEBUG_VIBRATOR) { - ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId()); - } + ALOGD_IF(DEBUG_VIBRATOR, "nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId()); std::list<NotifyArgs> out; mIndex += 1; if (size_t(mIndex) >= mSequence.pattern.size()) { @@ -111,16 +105,11 @@ std::list<NotifyArgs> VibratorInputMapper::nextStep() { const VibrationElement& element = mSequence.pattern[mIndex]; if (element.isOn()) { - if (DEBUG_VIBRATOR) { - std::string description = element.toString(); - ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(), - description.c_str()); - } + ALOGD_IF(DEBUG_VIBRATOR, "nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(), + element.toString().c_str()); getDeviceContext().vibrate(element); } else { - if (DEBUG_VIBRATOR) { - ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); - } + ALOGD_IF(DEBUG_VIBRATOR, "nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); getDeviceContext().cancelVibrate(); } nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); @@ -128,17 +117,13 @@ std::list<NotifyArgs> VibratorInputMapper::nextStep() { std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration); mNextStepTime = now + duration.count(); getContext()->requestTimeoutAtTime(mNextStepTime); - if (DEBUG_VIBRATOR) { - ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count()); - } + ALOGD_IF(DEBUG_VIBRATOR, "nextStep: scheduled timeout in %lldms", element.duration.count()); return out; } NotifyVibratorStateArgs VibratorInputMapper::stopVibrating() { mVibrating = false; - if (DEBUG_VIBRATOR) { - ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); - } + ALOGD_IF(DEBUG_VIBRATOR, "stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); getDeviceContext().cancelVibrate(); // Request InputReader to notify InputManagerService for vibration complete. diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 480e27604a..3255877cf1 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -39,9 +39,6 @@ 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 = @@ -226,8 +223,7 @@ std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTim 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)) { + if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) { enableTapToClick(when); } } @@ -278,7 +274,7 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ // return early to prevent this tap return out; } - } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) { + } else if (mReaderContext.isPreventingTouchpadTaps()) { enableTapToClick(when); if (gesture.details.buttons.is_tap) { // return early to prevent this tap diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp index 26028c5643..51905f92cf 100644 --- a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp +++ b/services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp @@ -22,29 +22,17 @@ #include <log/log.h> +#include "Logging.h" #include "include/gestures.h" extern "C" { -namespace { - -/** - * Log details of each gesture output by the gestures library. - * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires - * restarting the shell) - */ -const bool DEBUG_TOUCHPAD_GESTURES = - __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures", - ANDROID_LOG_INFO); - -} // namespace - void gestures_log(int verb, const char* fmt, ...) { va_list args; va_start(args, fmt); if (verb == GESTURES_LOG_ERROR) { LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args); - } else if (DEBUG_TOUCHPAD_GESTURES) { + } else if (android::debugTouchpadGestures()) { if (verb == GESTURES_LOG_INFO) { LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args); } else { diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp index 6885adb242..3e62f368a4 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp @@ -25,12 +25,8 @@ #include <com_android_input_flags.h> #include <linux/input-event-codes.h> -namespace input_flags = com::android::input::flags; - namespace android { -const bool REPORT_PALMS_TO_GESTURES_LIBRARY = input_flags::report_palms_to_gestures_library(); - HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext, MultiTouchMotionAccumulator& motionAccumulator) : mDeviceContext(deviceContext), @@ -81,18 +77,11 @@ SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t } schs.fingers.clear(); - size_t numPalms = 0; for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) { MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i); if (!slot.isInUse()) { continue; } - // Some touchpads continue to report contacts even after they've identified them as palms. - // We want to exclude these contacts from the HardwareStates. - if (!REPORT_PALMS_TO_GESTURES_LIBRARY && slot.getToolType() == ToolType::PALM) { - numPalms++; - continue; - } FingerState& fingerState = schs.fingers.emplace_back(); fingerState = {}; @@ -105,15 +94,13 @@ SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t fingerState.position_x = slot.getX(); fingerState.position_y = slot.getY(); fingerState.tracking_id = slot.getTrackingId(); - if (REPORT_PALMS_TO_GESTURES_LIBRARY) { - fingerState.tool_type = slot.getToolType() == ToolType::PALM - ? FingerState::ToolType::kPalm - : FingerState::ToolType::kFinger; - } + fingerState.tool_type = slot.getToolType() == ToolType::PALM + ? FingerState::ToolType::kPalm + : FingerState::ToolType::kFinger; } schs.state.fingers = schs.fingers.data(); schs.state.finger_cnt = schs.fingers.size(); - schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount() - numPalms; + schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount(); return schs; } diff --git a/services/inputflinger/reader/mapper/gestures/Logging.cpp b/services/inputflinger/reader/mapper/gestures/Logging.cpp new file mode 100644 index 0000000000..b9b97c330c --- /dev/null +++ b/services/inputflinger/reader/mapper/gestures/Logging.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2025 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 "Logging.h" + +#include <android-base/properties.h> +#include <log/log.h> + +namespace { + +const bool IS_DEBUGGABLE_BUILD = +#if defined(__ANDROID__) + android::base::GetBoolProperty("ro.debuggable", false); +#else + true; +#endif + +} // namespace + +namespace android { + +bool debugTouchpadGestures() { + if (!IS_DEBUGGABLE_BUILD) { + static const bool DEBUG_RAW_EVENTS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures", + ANDROID_LOG_INFO); + return DEBUG_RAW_EVENTS; + } + return __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures", + ANDROID_LOG_INFO); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/gestures/Logging.h b/services/inputflinger/reader/mapper/gestures/Logging.h new file mode 100644 index 0000000000..db59fb3dca --- /dev/null +++ b/services/inputflinger/reader/mapper/gestures/Logging.h @@ -0,0 +1,29 @@ +/* + * Copyright 2025 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 { + +/** + * Log details of touchpad gesture library input, output, and processing. + * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG". + * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately + * on debuggable builds (e.g. userdebug). + */ +bool debugTouchpadGestures(); + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 18d47f648b..677cf1e6c4 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -52,6 +52,7 @@ cc_test { "AnrTracker_test.cpp", "CapturedTouchpadEventConverter_test.cpp", "CursorInputMapper_test.cpp", + "DisplayTopologyGraph_test.cpp", "EventHub_test.cpp", "FakeEventHub.cpp", "FakeInputReaderPolicy.cpp", diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp index 353011aa5c..c6246d9eec 100644 --- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp +++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp @@ -33,8 +33,6 @@ #include "TestEventMatchers.h" #include "TestInputListener.h" -namespace input_flags = com::android::input::flags; - namespace android { using testing::AllOf; @@ -50,8 +48,6 @@ public: mReader(mFakeEventHub, mFakePolicy, mFakeListener), mDevice(newDevice()), mDeviceContext(*mDevice, EVENTHUB_ID) { - input_flags::include_relative_axis_values_for_captured_touchpads(true); - const size_t slotCount = 8; mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0); mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true); diff --git a/services/inputflinger/tests/DisplayTopologyGraph_test.cpp b/services/inputflinger/tests/DisplayTopologyGraph_test.cpp new file mode 100644 index 0000000000..fd2f21c4cb --- /dev/null +++ b/services/inputflinger/tests/DisplayTopologyGraph_test.cpp @@ -0,0 +1,120 @@ +/* + * Copyright 2025 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 <input/DisplayTopologyGraph.h> + +#include <string> +#include <string_view> +#include <tuple> + +namespace android { + +namespace { + +constexpr ui::LogicalDisplayId DISPLAY_ID_1{1}; +constexpr ui::LogicalDisplayId DISPLAY_ID_2{2}; +constexpr int DENSITY_MEDIUM = 160; + +} // namespace + +using DisplayTopologyGraphTestFixtureParam = + std::tuple<std::string_view /*name*/, DisplayTopologyGraph, bool /*isValid*/>; + +class DisplayTopologyGraphTestFixture + : public testing::Test, + public testing::WithParamInterface<DisplayTopologyGraphTestFixtureParam> {}; + +TEST_P(DisplayTopologyGraphTestFixture, DisplayTopologyGraphTest) { + const auto& [_, displayTopology, isValid] = GetParam(); + EXPECT_EQ(isValid, displayTopology.isValid()); +} + +INSTANTIATE_TEST_SUITE_P( + DisplayTopologyGraphTest, DisplayTopologyGraphTestFixture, + testing::Values( + std::make_tuple( + "InvalidPrimaryDisplay", + DisplayTopologyGraph{.primaryDisplayId = ui::LogicalDisplayId::INVALID, + .graph = {}, + .displaysDensity = {}}, + false), + std::make_tuple("PrimaryDisplayNotInGraph", + DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, + .graph = {}, + .displaysDensity = {}}, + false), + std::make_tuple("DisplayDensityMissing", + DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, + .graph = {{DISPLAY_ID_1, {}}}, + .displaysDensity = {}}, + false), + std::make_tuple("ValidSingleDisplayTopology", + DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, + .graph = {{DISPLAY_ID_1, {}}}, + .displaysDensity = {{DISPLAY_ID_1, + DENSITY_MEDIUM}}}, + true), + std::make_tuple( + "MissingReverseEdge", + DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, + .graph = {{DISPLAY_ID_1, + {{DISPLAY_ID_2, + DisplayTopologyPosition::TOP, 0}}}}, + .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, + {DISPLAY_ID_2, DENSITY_MEDIUM}}}, + false), + std::make_tuple( + "IncorrectReverseEdgeDirection", + DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, + .graph = {{DISPLAY_ID_1, + {{DISPLAY_ID_2, + DisplayTopologyPosition::TOP, 0}}}, + {DISPLAY_ID_2, + {{DISPLAY_ID_1, + DisplayTopologyPosition::TOP, 0}}}}, + .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, + {DISPLAY_ID_2, DENSITY_MEDIUM}}}, + false), + std::make_tuple( + "IncorrectReverseEdgeOffset", + DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, + .graph = {{DISPLAY_ID_1, + {{DISPLAY_ID_2, + DisplayTopologyPosition::TOP, 10}}}, + {DISPLAY_ID_2, + {{DISPLAY_ID_1, + DisplayTopologyPosition::BOTTOM, 20}}}}, + .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, + {DISPLAY_ID_2, DENSITY_MEDIUM}}}, + false), + std::make_tuple( + "ValidMultiDisplayTopology", + DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, + .graph = {{DISPLAY_ID_1, + {{DISPLAY_ID_2, + DisplayTopologyPosition::TOP, 10}}}, + {DISPLAY_ID_2, + {{DISPLAY_ID_1, + DisplayTopologyPosition::BOTTOM, -10}}}}, + .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, + {DISPLAY_ID_2, DENSITY_MEDIUM}}}, + true)), + [](const testing::TestParamInfo<DisplayTopologyGraphTestFixtureParam>& p) { + return std::string{std::get<0>(p.param)}; + }); + +} // namespace android diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index c4257a83c3..dcb148fe41 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp @@ -198,10 +198,6 @@ void FakeInputDispatcherPolicy::assertNotifyInputChannelBrokenWasCalled(const sp ASSERT_EQ(token, *receivedToken); } -void FakeInputDispatcherPolicy::setInterceptKeyTimeout(std::chrono::milliseconds timeout) { - mInterceptKeyTimeout = timeout; -} - std::chrono::nanoseconds FakeInputDispatcherPolicy::getKeyWaitingForEventsTimeout() { return 500ms; } @@ -210,8 +206,9 @@ void FakeInputDispatcherPolicy::setStaleEventTimeout(std::chrono::nanoseconds ti mStaleEventTimeout = timeout; } -void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) { - mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching; +void FakeInputDispatcherPolicy::setInterceptKeyBeforeDispatchingResult( + std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> result) { + mInterceptKeyBeforeDispatchingResult = result; } void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) { @@ -404,7 +401,9 @@ bool FakeInputDispatcherPolicy::filterInputEvent(const InputEvent& inputEvent, void FakeInputDispatcherPolicy::interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) { if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) { // Clear intercept state when we handled the event. - mInterceptKeyTimeout = 0ms; + if (std::holds_alternative<nsecs_t>(mInterceptKeyBeforeDispatchingResult)) { + mInterceptKeyBeforeDispatchingResult = nsecs_t(0); + } } } @@ -414,17 +413,20 @@ void FakeInputDispatcherPolicy::interceptMotionBeforeQueueing(ui::LogicalDisplay std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) { - if (mConsumeKeyBeforeDispatching) { - return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP; + if (std::holds_alternative<inputdispatcher::KeyEntry::InterceptKeyResult>( + mInterceptKeyBeforeDispatchingResult)) { + return mInterceptKeyBeforeDispatchingResult; } - nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count(); + nsecs_t delay = + std::chrono::nanoseconds(std::get<nsecs_t>(mInterceptKeyBeforeDispatchingResult)) + .count(); if (delay == 0) { return inputdispatcher::KeyEntry::InterceptKeyResult::CONTINUE; } // Clear intercept state so we could dispatch the event in next wake. - mInterceptKeyTimeout = 0ms; + mInterceptKeyBeforeDispatchingResult = nsecs_t(0); return delay; } diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index c387eacb51..b151686e9b 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -96,10 +96,6 @@ public: void assertDropTargetEquals(const InputDispatcherInterface& dispatcher, const sp<IBinder>& targetToken); void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token); - /** - * Set policy timeout. A value of zero means next key will not be intercepted. - */ - void setInterceptKeyTimeout(std::chrono::milliseconds timeout); std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override; void setStaleEventTimeout(std::chrono::nanoseconds timeout); void assertUserActivityNotPoked(); @@ -116,7 +112,12 @@ public: void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler); void assertUnhandledKeyReported(int32_t keycode); void assertUnhandledKeyNotReported(); - void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching); + /** + * Set policy timeout or the interception result. + * A timeout value of zero means next key will not be intercepted. + */ + void setInterceptKeyBeforeDispatchingResult( + std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> result); void assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay); private: @@ -145,11 +146,10 @@ private: std::condition_variable mNotifyUserActivity; std::queue<UserActivityPokeEvent> mUserActivityPokeEvents; - std::chrono::milliseconds mInterceptKeyTimeout = 0ms; - std::chrono::nanoseconds mStaleEventTimeout = 1000ms; - bool mConsumeKeyBeforeDispatching = false; + std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> + mInterceptKeyBeforeDispatchingResult; BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions; diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 914f5abdbd..35310a529b 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -42,8 +42,6 @@ namespace input_flags = com::android::input::flags; 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); @@ -1461,7 +1459,6 @@ TEST_F(GestureConverterTest, Click) { } TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION), REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) { nsecs_t currentTime = ARBITRARY_GESTURE_TIME; @@ -1574,8 +1571,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithRelativeMotion(0.f, 0.f)))); } -TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { +TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) { // Click should still produce button press/release events mReader->getContext()->setPreventingTouchpadTaps(true); @@ -1644,8 +1640,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); } -TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick, - REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { +TEST_F(GestureConverterTest, MoveEnablesTapToClick) { // initially disable tap-to-click mReader->getContext()->setPreventingTouchpadTaps(true); diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp index 34c81fcf02..ee125bb2cb 100644 --- a/services/inputflinger/tests/HardwareStateConverter_test.cpp +++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp @@ -33,13 +33,6 @@ namespace android { -namespace { - -const auto REPORT_PALMS = - ACONFIG_FLAG(com::android::input::flags, report_palms_to_gestures_library); - -} // namespace - class HardwareStateConverterTest : public testing::Test { public: HardwareStateConverterTest() @@ -201,24 +194,7 @@ TEST_F(HardwareStateConverterTest, TwoFingers) { EXPECT_EQ(0u, finger2.flags); } -TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmDisableReportPalms, - REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) { - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100); - - processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1); - processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1); - std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME); - ASSERT_TRUE(schs.has_value()); - EXPECT_EQ(0, schs->state.touch_cnt); - EXPECT_EQ(0, schs->state.finger_cnt); -} - -TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmEnableReportPalms, - REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) { +TEST_F(HardwareStateConverterTest, OnePalm) { processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0); processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123); @@ -234,54 +210,7 @@ TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmEnableReportPalms, EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type); } -TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmDisableReportPalms, - REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) { - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100); - - processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1); - processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1); - - std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME); - ASSERT_TRUE(schs.has_value()); - EXPECT_EQ(1, schs->state.touch_cnt); - EXPECT_EQ(1, schs->state.finger_cnt); - - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 51); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 99); - - schs = processSync(ARBITRARY_TIME); - ASSERT_TRUE(schs.has_value()); - EXPECT_EQ(0, schs->state.touch_cnt); - ASSERT_EQ(0, schs->state.finger_cnt); - - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 53); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 97); - - schs = processSync(ARBITRARY_TIME); - ASSERT_TRUE(schs.has_value()); - EXPECT_EQ(0, schs->state.touch_cnt); - EXPECT_EQ(0, schs->state.finger_cnt); - - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 55); - processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 95); - schs = processSync(ARBITRARY_TIME); - ASSERT_TRUE(schs.has_value()); - EXPECT_EQ(1, schs->state.touch_cnt); - ASSERT_EQ(1, schs->state.finger_cnt); - const FingerState& newFinger = schs->state.fingers[0]; - EXPECT_EQ(123, newFinger.tracking_id); - EXPECT_NEAR(55, newFinger.position_x, EPSILON); - EXPECT_NEAR(95, newFinger.position_y, EPSILON); -} - -TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms, - REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) { +TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms) { processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0); processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 7cc4ff76b8..f7dcd6c6e3 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -37,6 +37,7 @@ #include <input/BlockingQueue.h> #include <input/Input.h> #include <input/InputConsumer.h> +#include <input/KeyCharacterMap.h> #include <input/PrintTools.h> #include <linux/input.h> #include <sys/epoll.h> @@ -139,6 +140,30 @@ static KeyEvent getTestKeyEvent() { return event; } +InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId, DeviceId deviceId) { + InputDeviceIdentifier identifier; + identifier.vendor = vendorId; + identifier.product = productId; + auto info = InputDeviceInfo(); + info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device", + /*isExternal=*/false, /*hasMic=*/false, ui::LogicalDisplayId::INVALID); + return info; +} + +std::unique_ptr<KeyCharacterMap> loadKeyCharacterMap(const char* name) { + InputDeviceIdentifier identifier; + identifier.name = name; + std::string path = getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(), + InputDeviceConfigurationFileType:: + KEY_CHARACTER_MAP); + + if (path.empty()) { + return nullptr; + } + + return *KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE); +} + } // namespace // --- InputDispatcherTest --- @@ -5491,7 +5516,8 @@ TEST_F(InputDispatcherTest, InterceptKeyByPolicy) { generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT); const std::chrono::milliseconds interceptKeyTimeout = 50ms; const nsecs_t injectTime = keyArgs.eventTime; - mFakePolicy->setInterceptKeyTimeout(interceptKeyTimeout); + mFakePolicy->setInterceptKeyBeforeDispatchingResult( + std::chrono::nanoseconds(interceptKeyTimeout).count()); mDispatcher->notifyKey(keyArgs); // The dispatching time should be always greater than or equal to intercept key timeout. window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT); @@ -5519,7 +5545,7 @@ TEST_F(InputDispatcherTest, InterceptKeyIfKeyUp) { // Set a value that's significantly larger than the default consumption timeout. If the // implementation is correct, the actual value doesn't matter; it won't slow down the test. - mFakePolicy->setInterceptKeyTimeout(600ms); + mFakePolicy->setInterceptKeyBeforeDispatchingResult(std::chrono::nanoseconds(600ms).count()); mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT)); // Window should receive key event immediately when same key up. window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT); @@ -7438,7 +7464,8 @@ TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyConsumedKey) { window->consumeFocusEvent(true); - mFakePolicy->setConsumeKeyBeforeDispatching(true); + mFakePolicy->setInterceptKeyBeforeDispatchingResult( + inputdispatcher::KeyEntry::InterceptKeyResult::SKIP); mDispatcher->notifyKey( KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); @@ -7464,7 +7491,8 @@ TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserAct window->consumeFocusEvent(true); - mFakePolicy->setConsumeKeyBeforeDispatching(true); + mFakePolicy->setInterceptKeyBeforeDispatchingResult( + inputdispatcher::KeyEntry::InterceptKeyResult::SKIP); mDispatcher->notifyKey( KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); @@ -7477,6 +7505,50 @@ TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserAct mFakePolicy->assertUserActivityPoked(); } +TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyFallbackKey) { +#if !defined(__ANDROID__) + GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device."; +#endif + InputDeviceInfo testDevice = generateTestDeviceInfo(/*vendorId=*/0, + /*productId=*/0, /*deviceId=*/1); + std::unique_ptr<KeyCharacterMap> kcm = loadKeyCharacterMap("Generic"); + ASSERT_NE(nullptr, kcm); + + testDevice.setKeyCharacterMap(std::move(kcm)); + mDispatcher->notifyInputDevicesChanged(NotifyInputDevicesChangedArgs(/*id=*/1, {testDevice})); + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", + ui::LogicalDisplayId::DEFAULT); + + window->setFocusable(true); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + + window->consumeFocusEvent(true); + + mFakePolicy->setInterceptKeyBeforeDispatchingResult( + inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK); + + // In the Generic KCM fallbacks, Meta + Space => SEARCH. + mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .keyCode(AKEYCODE_SPACE) + .metaState(AMETA_META_ON) + .build()); + mDispatcher->waitForIdle(); + + // Should have poked user activity + mFakePolicy->assertUserActivityPoked(); + + // Fallback is generated and sent instead. + std::unique_ptr<KeyEvent> consumedEvent = window->consumeKey(/*handled=*/false); + ASSERT_NE(nullptr, consumedEvent); + ASSERT_THAT(*consumedEvent, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_SEARCH), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); +} + class DisableUserActivityInputDispatcherTest : public InputDispatcherTest, public ::testing::WithParamInterface<bool> {}; @@ -9307,6 +9379,24 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) { mWindow->assertNoEvents(); } +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterFocusedWindowChanged) { + sp<FakeWindowHandle> anotherWindow = + sp<FakeWindowHandle>::make(mApp, mDispatcher, "AnotherWindow", + ui::LogicalDisplayId::DEFAULT); + anotherWindow->setFocusable(true); + mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *anotherWindow->getInfo()}, {}, 0, 0}); + + sendAndConsumeKeyDown(/*deviceId=*/1); + expectKeyRepeatOnce(/*repeatCount=*/1); + expectKeyRepeatOnce(/*repeatCount=*/2); + setFocusedWindow(anotherWindow); + anotherWindow->consumeFocusEvent(true); + + // Window should receive key up event with cancel. + mWindow->consumeKeyUp(ui::LogicalDisplayId::DEFAULT, AKEY_EVENT_FLAG_CANCELED); + anotherWindow->assertNoEvents(); +} + TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) { sendAndConsumeKeyDown(/*deviceId=*/1); expectKeyRepeatOnce(/*repeatCount=*/1); @@ -9924,6 +10014,9 @@ protected: virtual void SetUp() override { InputDispatcherTest::SetUp(); + // Use current time as start time otherwise events may be dropped due to being stale. + mGestureStartTime = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); application->setDispatchingTimeout(100ms); @@ -9941,82 +10034,81 @@ protected: mWindow->consumeFocusEvent(true); } - void notifyAndConsumeMotion(int32_t action, uint32_t source, ui::LogicalDisplayId displayId, - nsecs_t eventTime) { - mDispatcher->notifyMotion(MotionArgsBuilder(action, source) - .displayId(displayId) - .eventTime(eventTime) - .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) - .build()); + NotifyMotionArgs notifyAndConsumeMotion(int32_t action, uint32_t source, + ui::LogicalDisplayId displayId, + std::chrono::nanoseconds timeDelay) { + const NotifyMotionArgs motionArgs = + MotionArgsBuilder(action, source) + .displayId(displayId) + .eventTime((mGestureStartTime + timeDelay).count()) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build(); + mDispatcher->notifyMotion(motionArgs); mWindow->consumeMotionEvent(WithMotionAction(action)); + return motionArgs; } private: sp<FakeWindowHandle> mWindow; + std::chrono::nanoseconds mGestureStartTime; }; TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, MinPokeTimeObserved, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { - // Use current time otherwise events may be dropped due to being stale. - const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); - mDispatcher->setMinTimeBetweenUserActivityPokes(50ms); // First event of type TOUCH. Should poke. - notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(50)); + NotifyMotionArgs motionArgs = + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(50)); mFakePolicy->assertUserActivityPoked( - {{currentTime + milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH, - ui::LogicalDisplayId::DEFAULT}}); + {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}}); // 80ns > 50ns has passed since previous TOUCH event. Should poke. - notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(130)); + motionArgs = + notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(130)); mFakePolicy->assertUserActivityPoked( - {{currentTime + milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH, - ui::LogicalDisplayId::DEFAULT}}); + {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}}); // First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event). - notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, - ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(135)); + motionArgs = + notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, + ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(135)); mFakePolicy->assertUserActivityPoked( - {{currentTime + milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER, - ui::LogicalDisplayId::DEFAULT}}); + {{motionArgs.eventTime, USER_ACTIVITY_EVENT_OTHER, ui::LogicalDisplayId::DEFAULT}}); // Within 50ns of previous TOUCH event. Should NOT poke. notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(140)); + std::chrono::milliseconds(140)); mFakePolicy->assertUserActivityNotPoked(); // Within 50ns of previous OTHER event. Should NOT poke. notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, - ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(150)); + ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(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, ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(160)); + std::chrono::milliseconds(160)); mFakePolicy->assertUserActivityNotPoked(); // 65ns > 50ns has passed since previous OTHER event. Should poke. - notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, - ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(200)); + motionArgs = + notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, + ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200)); mFakePolicy->assertUserActivityPoked( - {{currentTime + milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER, - ui::LogicalDisplayId::DEFAULT}}); + {{motionArgs.eventTime, USER_ACTIVITY_EVENT_OTHER, ui::LogicalDisplayId::DEFAULT}}); // 170ns > 50ns has passed since previous TOUCH event. Should poke. - notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(300)); + motionArgs = + notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT, + std::chrono::milliseconds(300)); mFakePolicy->assertUserActivityPoked( - {{currentTime + milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH, - ui::LogicalDisplayId::DEFAULT}}); + {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}}); // Assert that there's no more user activity poke event. mFakePolicy->assertUserActivityNotPoked(); @@ -10026,39 +10118,35 @@ TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { - // Use current time otherwise events may be dropped due to being stale. - const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); - notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(200)); + NotifyMotionArgs motionArgs = + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200)); mFakePolicy->assertUserActivityPoked( - {{currentTime + milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH, - ui::LogicalDisplayId::DEFAULT}}); + {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}}); notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(280)); + std::chrono::milliseconds(280)); mFakePolicy->assertUserActivityNotPoked(); - notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, - currentTime + milliseconds_to_nanoseconds(340)); + motionArgs = + notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(340)); mFakePolicy->assertUserActivityPoked( - {{currentTime + milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH, - ui::LogicalDisplayId::DEFAULT}}); + {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}}); } TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { - // Use current time otherwise events may be dropped due to being stale. - const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); mDispatcher->setMinTimeBetweenUserActivityPokes(0ms); notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, - currentTime + 20); + std::chrono::milliseconds(20)); mFakePolicy->assertUserActivityPoked(); notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, - currentTime + 30); + std::chrono::milliseconds(30)); mFakePolicy->assertUserActivityPoked(); } @@ -12376,6 +12464,11 @@ protected: sp<FakeWindowHandle> mSecondWindow; sp<FakeWindowHandle> mDragWindow; sp<FakeWindowHandle> mSpyWindow; + + std::vector<gui::DisplayInfo> mDisplayInfos; + + std::shared_ptr<FakeApplicationHandle> mSecondApplication; + sp<FakeWindowHandle> mWindowOnSecondDisplay; // Mouse would force no-split, set the id as non-zero to verify if drag state could track it. static constexpr int32_t MOUSE_POINTER_ID = 1; @@ -12396,26 +12489,37 @@ protected: mSpyWindow->setTrustedOverlay(true); mSpyWindow->setFrame(Rect(0, 0, 200, 100)); + mSecondApplication = std::make_shared<FakeApplicationHandle>(); + mWindowOnSecondDisplay = + sp<FakeWindowHandle>::make(mSecondApplication, mDispatcher, + "TestWindowOnSecondDisplay", SECOND_DISPLAY_ID); + mWindowOnSecondDisplay->setFrame({0, 0, 100, 100}); + mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApp); mDispatcher->onWindowInfosChanged( - {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()}, - {}, + {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo(), + *mWindowOnSecondDisplay->getInfo()}, + mDisplayInfos, 0, 0}); } - void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { + void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN, + ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) { bool consumeButtonPress = false; + const PointF location = + displayId == ui::LogicalDisplayId::DEFAULT ? PointF(50, 50) : PointF(50, 450); switch (fromSource) { case AINPUT_SOURCE_TOUCHSCREEN: { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, - ui::LogicalDisplayId::DEFAULT, {50, 50})) + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, displayId, + location)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; break; } case AINPUT_SOURCE_STYLUS: { - PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(50).y(50); + PointerBuilder pointer = + PointerBuilder(0, ToolType::STYLUS).x(location.x).y(location.y); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, @@ -12438,12 +12542,14 @@ protected: break; } case AINPUT_SOURCE_MOUSE: { - PointerBuilder pointer = - PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50); + PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE) + .x(location.x) + .y(location.y); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .displayId(displayId) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(pointer) .build())); @@ -12451,6 +12557,7 @@ protected: injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) + .displayId(displayId) .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(pointer) @@ -12464,43 +12571,59 @@ protected: } // Window should receive motion event. - mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); + sp<FakeWindowHandle>& targetWindow = + displayId == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay; + targetWindow->consumeMotionDown(displayId); if (consumeButtonPress) { - mWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + targetWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + } + + // Spy window should also receive motion event if event is on the same display. + if (displayId == ui::LogicalDisplayId::DEFAULT) { + mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); } - // Spy window should also receive motion event - mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); } // Start performing drag, we will create a drag window and transfer touch to it. // @param sendDown : if true, send a motion down on first window before perform drag and drop. // Returns true on success. - bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { + bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN, + ui::LogicalDisplayId dragStartDisplay = ui::LogicalDisplayId::DEFAULT) { if (sendDown) { - injectDown(fromSource); + injectDown(fromSource, dragStartDisplay); } // The drag window covers the entire display - mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", - ui::LogicalDisplayId::DEFAULT); + mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", dragStartDisplay); mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}}); - mDispatcher->onWindowInfosChanged({{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), - *mWindow->getInfo(), *mSecondWindow->getInfo()}, - {}, - 0, - 0}); + mDispatcher->onWindowInfosChanged( + {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(), + *mSecondWindow->getInfo(), *mWindowOnSecondDisplay->getInfo()}, + mDisplayInfos, + 0, + 0}); + + sp<FakeWindowHandle>& targetWindow = dragStartDisplay == ui::LogicalDisplayId::DEFAULT + ? mWindow + : mWindowOnSecondDisplay; // Transfer touch focus to the drag window bool transferred = - mDispatcher->transferTouchGesture(mWindow->getToken(), mDragWindow->getToken(), + mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(), /*isDragDrop=*/true); if (transferred) { - mWindow->consumeMotionCancel(); - mDragWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, - AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + targetWindow->consumeMotionCancel(dragStartDisplay); + mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } return transferred; } + + void addDisplay(ui::LogicalDisplayId displayId, ui::Transform transform) { + gui::DisplayInfo displayInfo; + displayInfo.displayId = displayId; + displayInfo.transform = transform; + mDisplayInfos.push_back(displayInfo); + } }; TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { @@ -15187,4 +15310,230 @@ TEST_P(TransferOrDontTransferFixture, MouseAndTouchTransferSimultaneousMultiDevi INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, testing::Bool()); +class InputDispatcherConnectedDisplayTest : public InputDispatcherDragTests { + constexpr static int DENSITY_MEDIUM = 160; + + const DisplayTopologyGraph + mTopology{.primaryDisplayId = DISPLAY_ID, + .graph = {{DISPLAY_ID, + {{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}}, + {SECOND_DISPLAY_ID, + {{DISPLAY_ID, DisplayTopologyPosition::BOTTOM, 0.0f}}}}, + .displaysDensity = {{DISPLAY_ID, DENSITY_MEDIUM}, + {SECOND_DISPLAY_ID, DENSITY_MEDIUM}}}; + +protected: + void SetUp() override { + addDisplay(DISPLAY_ID, ui::Transform()); + addDisplay(SECOND_DISPLAY_ID, + ui::Transform(ui::Transform::ROT_270, /*logicalDisplayWidth=*/ + 500, /*logicalDisplayHeight=*/500)); + + InputDispatcherDragTests::SetUp(); + + mDispatcher->setDisplayTopology(mTopology); + } +}; + +TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseGesture) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + + // pointer-down + mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60)) + .build()); + mWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60))); + + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60)) + .build()); + mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60))); + + // pointer-move + mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60)) + .build()); + mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60))); + + // pointer-move with different display + // TODO (b/383092013): by default windows will not be topology aware and receive events as it + // was in the same display. This behaviour has not been implemented yet. + mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(SECOND_DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70)) + .build()); + // events should be delivered with the second displayId and in corrosponding coordinate space + mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 430))); + + // pointer-up + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, AINPUT_SOURCE_MOUSE) + .displayId(SECOND_DISPLAY_ID) + .buttonState(0) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70)) + .build()); + mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 430))); + + mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) + .displayId(SECOND_DISPLAY_ID) + .buttonState(0) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70)) + .build()); + mWindow->consumeMotionUp(SECOND_DISPLAY_ID); +} + +TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromPrimaryDisplay) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + + EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE)); + // Move on window. + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mWindow->consumeDragEvent(false, 50, 50); + mSecondWindow->assertNoEvents(); + mWindowOnSecondDisplay->assertNoEvents(); + + // Move to another window. + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(150).y(50)) + .build()); + mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mWindow->consumeDragEvent(true, 150, 50); + mSecondWindow->consumeDragEvent(false, 50, 50); + mWindowOnSecondDisplay->assertNoEvents(); + + // Move to window on the second display + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(SECOND_DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mWindow->assertNoEvents(); + mSecondWindow->consumeDragEvent(true, -50, 50); + mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50); + + // drop on the second display + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) + .displayId(SECOND_DISPLAY_ID) + .buttonState(0) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionUp(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindowOnSecondDisplay->getToken()); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mWindowOnSecondDisplay->assertNoEvents(); +} + +TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromNonPrimaryDisplay) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + + EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)); + // Move on window. + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(SECOND_DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50); + + // Move to window on the primary display + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mWindow->consumeDragEvent(false, 50, 50); + mSecondWindow->assertNoEvents(); + mWindowOnSecondDisplay->consumeDragEvent(true, 50, 50); + + // drop on the primary display + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(0) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionUp(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken()); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mWindowOnSecondDisplay->assertNoEvents(); +} + +using InputDispatcherConnectedDisplayPointerInWindowTest = InputDispatcherConnectedDisplayTest; + +TEST_F(InputDispatcherConnectedDisplayPointerInWindowTest, MouseOnWindowOnPrimaryDisplay) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50)) + .build()); + + mWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + mWindowOnSecondDisplay->assertNoEvents(); + + ASSERT_TRUE(mDispatcher->isPointerInWindow(mWindow->getToken(), DISPLAY_ID, DEVICE_ID, + /*pointerId=*/0)); + ASSERT_TRUE(mDispatcher->isPointerInWindow(mSpyWindow->getToken(), DISPLAY_ID, DEVICE_ID, + /*pointerId=*/0)); + ASSERT_FALSE(mDispatcher->isPointerInWindow(mWindowOnSecondDisplay->getToken(), + SECOND_DISPLAY_ID, DEVICE_ID, /*pointerId=*/0)); +} + +TEST_F(InputDispatcherConnectedDisplayPointerInWindowTest, MouseOnWindowOnNonPrimaryDisplay) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .displayId(SECOND_DISPLAY_ID) + .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50)) + .build()); + + mWindow->assertNoEvents(); + mSpyWindow->assertNoEvents(); + mWindowOnSecondDisplay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + ASSERT_FALSE(mDispatcher->isPointerInWindow(mWindow->getToken(), DISPLAY_ID, DEVICE_ID, + /*pointerId=*/0)); + ASSERT_FALSE(mDispatcher->isPointerInWindow(mSpyWindow->getToken(), DISPLAY_ID, DEVICE_ID, + /*pointerId=*/0)); + ASSERT_TRUE(mDispatcher->isPointerInWindow(mWindowOnSecondDisplay->getToken(), + SECOND_DISPLAY_ID, DEVICE_ID, /*pointerId=*/0)); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index dce54728ed..d4e4bb00f7 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -50,6 +50,8 @@ namespace android { class MockInputReaderContext : public InputReaderContext { public: + std::string dump() override { return "(dump from MockInputReaderContext)"; } + MOCK_METHOD(void, updateGlobalMetaState, (), (override)); MOCK_METHOD(int32_t, getGlobalMetaState, (), (override)); @@ -191,6 +193,9 @@ public: (ui::LogicalDisplayId displayId, const vec2& position), (override)); MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override)); MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override)); + MOCK_METHOD(std::optional<vec2>, filterPointerMotionForAccessibility, + (const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId), + (override)); }; class MockInputDevice : public InputDevice { diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index 17acdd49cf..d4b15bca48 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -176,16 +176,22 @@ protected: return std::get<NotifyKeyArgs>(args.front()); } - void testDPadKeyRotation(int32_t originalEvdevCode, int32_t originalKeyCode, - int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = DISPLAY_ID) { - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 1); + std::list<NotifyArgs> processKeyAndSync(nsecs_t when, int32_t code, int32_t value) { + std::list<NotifyArgs> argsList = process(when, EV_KEY, code, value); + argsList += process(when, EV_SYN, SYN_REPORT, 0); + return argsList; + } + + void testDPadKeyRotation(int32_t originalEvdevCode, int32_t rotatedKeyCode, + ui::LogicalDisplayId displayId = DISPLAY_ID) { + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, originalEvdevCode, 1); NotifyKeyArgs args = expectSingleKeyArg(argsList); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); ASSERT_EQ(originalEvdevCode, args.scanCode); ASSERT_EQ(rotatedKeyCode, args.keyCode); ASSERT_EQ(displayId, args.displayId); - argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, originalEvdevCode, 0); args = expectSingleKeyArg(argsList); ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); ASSERT_EQ(originalEvdevCode, args.scanCode); @@ -205,23 +211,16 @@ TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) { .With(Args<0>(when)) .Times(keyCodes.size()); for (int32_t keyCode : keyCodes) { - process(when, EV_KEY, keyCode, 1); - process(when, EV_SYN, SYN_REPORT, 0); - process(when, EV_KEY, keyCode, 0); - process(when, EV_SYN, SYN_REPORT, 0); + processKeyAndSync(when, keyCode, 1); + processKeyAndSync(when, keyCode, 0); } } TEST_F(KeyboardInputMapperUnitTest, RepeatEventsDiscarded) { std::list<NotifyArgs> args; - args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 1); - args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); - - args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 2); - args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); - - args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 0); - args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 1); + args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 2); + args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), @@ -241,7 +240,7 @@ TEST_F(KeyboardInputMapperUnitTest, Process_SimpleKeyPress) { ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); // Key down by evdev code. - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1); NotifyKeyArgs args = expectSingleKeyArg(argsList); ASSERT_EQ(DEVICE_ID, args.deviceId); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); @@ -255,7 +254,7 @@ TEST_F(KeyboardInputMapperUnitTest, Process_SimpleKeyPress) { ASSERT_EQ(ARBITRARY_TIME, args.downTime); // Key up by evdev code. - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0); args = expectSingleKeyArg(argsList); ASSERT_EQ(DEVICE_ID, args.deviceId); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); @@ -271,6 +270,7 @@ TEST_F(KeyboardInputMapperUnitTest, Process_SimpleKeyPress) { // Key down by usage code. argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A); argsList += process(ARBITRARY_TIME, EV_KEY, 0, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); args = expectSingleKeyArg(argsList); ASSERT_EQ(DEVICE_ID, args.deviceId); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); @@ -284,8 +284,9 @@ TEST_F(KeyboardInputMapperUnitTest, Process_SimpleKeyPress) { ASSERT_EQ(ARBITRARY_TIME, args.downTime); // Key up by usage code. - argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A); + argsList = process(ARBITRARY_TIME + 1, EV_MSC, MSC_SCAN, USAGE_A); argsList += process(ARBITRARY_TIME + 1, EV_KEY, 0, 0); + argsList += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0); args = expectSingleKeyArg(argsList); ASSERT_EQ(DEVICE_ID, args.deviceId); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); @@ -307,6 +308,7 @@ TEST_F(KeyboardInputMapperUnitTest, Process_UnknownKey) { // Key down with unknown scan code or usage code. std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); NotifyKeyArgs args = expectSingleKeyArg(argsList); ASSERT_EQ(DEVICE_ID, args.deviceId); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); @@ -320,8 +322,9 @@ TEST_F(KeyboardInputMapperUnitTest, Process_UnknownKey) { ASSERT_EQ(ARBITRARY_TIME, args.downTime); // Key up with unknown scan code or usage code. - argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); + argsList = process(ARBITRARY_TIME + 1, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0); + argsList += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0); args = expectSingleKeyArg(argsList); ASSERT_EQ(DEVICE_ID, args.deviceId); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); @@ -343,10 +346,12 @@ TEST_F(KeyboardInputMapperUnitTest, Process_SendsReadTime) { // Key down std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1); + argsList += process(ARBITRARY_TIME, /*readTime=*/12, EV_SYN, SYN_REPORT, 0); ASSERT_EQ(12, expectSingleKeyArg(argsList).readTime); // Key up - argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1); + argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 0); + argsList += process(ARBITRARY_TIME, /*readTime=*/15, EV_SYN, SYN_REPORT, 0); ASSERT_EQ(15, expectSingleKeyArg(argsList).readTime); } @@ -360,22 +365,22 @@ TEST_F(KeyboardInputMapperUnitTest, Process_ShouldUpdateMetaState) { ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); // Metakey down. - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_LEFTSHIFT, 1); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); // Key down. - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 1); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); // Key up. - argsList = process(ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 2, KEY_A, 0); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); // Metakey up. - argsList = process(ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 3, KEY_LEFTSHIFT, 0); ASSERT_EQ(AMETA_NONE, expectSingleKeyArg(argsList).metaState); ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); } @@ -387,11 +392,10 @@ TEST_F(KeyboardInputMapperUnitTest, Process_WhenNotOrientationAware_ShouldNotRot addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT); setDisplayOrientation(ui::Rotation::Rotation90); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT)); } TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPad) { @@ -405,43 +409,40 @@ TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPa AINPUT_SOURCE_KEYBOARD); setDisplayOrientation(ui::ROTATION_0); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT)); setDisplayOrientation(ui::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_DOWN)); setDisplayOrientation(ui::ROTATION_180); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_RIGHT)); setDisplayOrientation(ui::ROTATION_270); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_UP)); // Special case: if orientation changes while key is down, we still emit the same keycode // in the key up as we did in the key down. setDisplayOrientation(ui::ROTATION_270); - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1); NotifyKeyArgs args = expectSingleKeyArg(argsList); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); ASSERT_EQ(KEY_UP, args.scanCode); ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); setDisplayOrientation(ui::ROTATION_180); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0); args = expectSingleKeyArg(argsList); ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); ASSERT_EQ(KEY_UP, args.scanCode); @@ -454,9 +455,9 @@ TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_NotOrientationA addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); // Display id should be LogicalDisplayId::INVALID without any display configuration. - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1); ASSERT_GT(argsList.size(), 0u); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0); ASSERT_GT(argsList.size(), 0u); ASSERT_EQ(ui::LogicalDisplayId::INVALID, std::get<NotifyKeyArgs>(argsList.front()).displayId); } @@ -474,9 +475,9 @@ TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_OrientationAwar // ^--- already checked by the previous test setDisplayOrientation(ui::ROTATION_0); - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1); ASSERT_GT(argsList.size(), 0u); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0); ASSERT_GT(argsList.size(), 0u); ASSERT_EQ(DISPLAY_ID, std::get<NotifyKeyArgs>(argsList.front()).displayId); @@ -487,9 +488,9 @@ TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_OrientationAwar argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); ASSERT_EQ(0u, argsList.size()); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1); ASSERT_GT(argsList.size(), 0u); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0); ASSERT_GT(argsList.size(), 0u); ASSERT_EQ(newDisplayId, std::get<NotifyKeyArgs>(argsList.front()).displayId); } @@ -565,48 +566,48 @@ TEST_F(KeyboardInputMapperUnitTest, Process_LockedKeysShouldToggleMetaStateAndLe ASSERT_FALSE(scrollLockLed); // Toggle caps lock on. - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 0); ASSERT_TRUE(capsLockLed); ASSERT_FALSE(numLockLed); ASSERT_FALSE(scrollLockLed); ASSERT_EQ(AMETA_CAPS_LOCK_ON, mMapper->getMetaState()); // Toggle num lock on. - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 0); ASSERT_TRUE(capsLockLed); ASSERT_TRUE(numLockLed); ASSERT_FALSE(scrollLockLed); ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mMapper->getMetaState()); // Toggle caps lock off. - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 0); ASSERT_FALSE(capsLockLed); ASSERT_TRUE(numLockLed); ASSERT_FALSE(scrollLockLed); ASSERT_EQ(AMETA_NUM_LOCK_ON, mMapper->getMetaState()); // Toggle scroll lock on. - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 0); ASSERT_FALSE(capsLockLed); ASSERT_TRUE(numLockLed); ASSERT_TRUE(scrollLockLed); ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mMapper->getMetaState()); // Toggle num lock off. - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 0); ASSERT_FALSE(capsLockLed); ASSERT_FALSE(numLockLed); ASSERT_TRUE(scrollLockLed); ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mMapper->getMetaState()); // Toggle scroll lock off. - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 0); ASSERT_FALSE(capsLockLed); ASSERT_FALSE(numLockLed); ASSERT_FALSE(scrollLockLed); @@ -618,8 +619,8 @@ TEST_F(KeyboardInputMapperUnitTest, DisablingDeviceResetsPressedKeys) { addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); - // Key down by scan code. - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + // Key down by evdev code. + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1); NotifyKeyArgs args = expectSingleKeyArg(argsList); ASSERT_EQ(DEVICE_ID, args.deviceId); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); @@ -685,37 +686,168 @@ TEST_F(KeyboardInputMapperUnitTest, Process_GestureEventToSetFlagKeepTouchMode) addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE); // Key down - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFT, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_LEFT, 1); ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, expectSingleKeyArg(argsList).flags); } -TEST_F_WITH_FLAGS(KeyboardInputMapperUnitTest, WakeBehavior_AlphabeticKeyboard, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, - enable_alphabetic_keyboard_wake))) { - // For internal alphabetic devices, keys will trigger wake on key down. +// --- KeyboardInputMapperUnitTest_WakeFlagOverride --- + +class KeyboardInputMapperUnitTest_WakeFlagOverride : public KeyboardInputMapperUnitTest { +protected: + virtual void SetUp() override { + SetUp(/*wakeFlag=*/com::android::input::flags::enable_alphabetic_keyboard_wake()); + } + + void SetUp(bool wakeFlag) { + mWakeFlagInitialValue = com::android::input::flags::enable_alphabetic_keyboard_wake(); + com::android::input::flags::enable_alphabetic_keyboard_wake(wakeFlag); + KeyboardInputMapperUnitTest::SetUp(); + } + + void TearDown() override { + com::android::input::flags::enable_alphabetic_keyboard_wake(mWakeFlagInitialValue); + KeyboardInputMapperUnitTest::TearDown(); + } + + bool mWakeFlagInitialValue; +}; + +// --- KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled --- + +class KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled + : public KeyboardInputMapperUnitTest_WakeFlagOverride { +protected: + void SetUp() override { + KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/true); + } +}; + +TEST_F(KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled, + NonAlphabeticDevice_WakeBehavior) { + // For internal non-alphabetic devices keys will not trigger wake. addKeyByEvdevCode(KEY_A, AKEYCODE_A); addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE); - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_A, 1); - ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 0); - ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); - ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); - ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); - ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); - ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); +} + +// --- KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled --- + +class KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled + : public KeyboardInputMapperUnitTest_WakeFlagOverride { +protected: + void SetUp() override { + KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/true); + + ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC)); + } +}; + +TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled, WakeBehavior) { + // For internal alphabetic devices, keys will trigger wake on key down when + // flag is enabled. + addKeyByEvdevCode(KEY_A, AKEYCODE_A); + addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); + addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE); + + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE)))); + + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); + + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE)))); + + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); + + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE)))); + + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); +} + +TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled, WakeBehavior_UnknownKey) { + // For internal alphabetic devices, unknown keys will trigger wake on key down when + // flag is enabled. + + const int32_t USAGE_UNKNOWN = 0x07ffff; + EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, KEY_UNKNOWN, USAGE_UNKNOWN, _, _, _, _)) + .WillRepeatedly(Return(NAME_NOT_FOUND)); + + // Key down with unknown scan code or usage code. + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); + argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE)))); + + // Key up with unknown scan code or usage code. + argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); + argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); +} + +// --- KeyboardInputMapperUnitTest_AlphabeticDevice_AlphabeticKeyboardWakeDisabled --- + +class KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagDisabled + : public KeyboardInputMapperUnitTest_WakeFlagOverride { +protected: + void SetUp() override { + KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/false); + + ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC)); + } +}; + +TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagDisabled, WakeBehavior) { + // For internal alphabetic devices, keys will not trigger wake when flag is + // disabled. + + addKeyByEvdevCode(KEY_A, AKEYCODE_A); + addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); + addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE); + + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); + + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); + + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); + + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); + + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); + + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0); + EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U)))); } // --- KeyboardInputMapperTest --- @@ -730,28 +862,33 @@ protected: } const std::string UNIQUE_ID = "local:0"; - void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode, - int32_t originalKeyCode, int32_t rotatedKeyCode, + void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalEvdevCode, + int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID); + + void processKeyAndSync(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t code, + int32_t value) { + process(mapper, when, readTime, EV_KEY, code, value); + process(mapper, when, readTime, EV_SYN, SYN_REPORT, 0); + } }; void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper, - int32_t originalScanCode, int32_t originalKeyCode, - int32_t rotatedKeyCode, + int32_t originalEvdevCode, int32_t rotatedKeyCode, ui::LogicalDisplayId displayId) { NotifyKeyArgs args; - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, originalEvdevCode, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(originalScanCode, args.scanCode); + ASSERT_EQ(originalEvdevCode, args.scanCode); ASSERT_EQ(rotatedKeyCode, args.keyCode); ASSERT_EQ(displayId, args.displayId); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, originalEvdevCode, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(originalScanCode, args.scanCode); + ASSERT_EQ(originalEvdevCode, args.scanCode); ASSERT_EQ(rotatedKeyCode, args.keyCode); ASSERT_EQ(displayId, args.displayId); } @@ -819,23 +956,19 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { ASSERT_TRUE(device2->isEnabled()); // Test pad key events + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, - AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, - AKEYCODE_DPAD_DOWN, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, - AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, newDisplayId)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, newDisplayId)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, newDisplayId)); ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, - AKEYCODE_DPAD_RIGHT, newDisplayId)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, - AKEYCODE_DPAD_DOWN, newDisplayId)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, - AKEYCODE_DPAD_LEFT, newDisplayId)); + testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, newDisplayId)); } TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { @@ -857,20 +990,20 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); // Toggle caps lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0); ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); // Toggle num lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0); ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); // Toggle scroll lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0); ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); @@ -937,16 +1070,16 @@ TEST_F(KeyboardInputMapperTest, Process_ResetLockedModifierState) { ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); // Toggle caps lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0); // Toggle num lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0); // Toggle scroll lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1); + processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0); ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); mReader->resetLockedModifierState(); @@ -996,40 +1129,40 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) { ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); // Toggle num lock on and off. - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0); ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState()); ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState()); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0); ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); // Toggle caps lock on and off. - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0); ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState()); ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState()); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0); ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); // Toggle scroll lock on and off. - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0); ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState()); ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState()); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1); + processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0); ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); @@ -1048,10 +1181,10 @@ TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) { KeyboardInputMapper& keyboardMapper = constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); + processKeyAndSync(keyboardMapper, ARBITRARY_TIME, 0, KEY_HOME, 1); ASSERT_NO_FATAL_FAILURE( mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD))); - process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); + processKeyAndSync(keyboardMapper, ARBITRARY_TIME, 0, KEY_HOME, 0); ASSERT_NO_FATAL_FAILURE( mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD))); @@ -1059,10 +1192,10 @@ TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) { KeyboardInputMapper& dpadMapper = constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD); for (auto* mapper : {&keyboardMapper, &dpadMapper}) { - process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); + processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD))); - process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); + processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD))); } @@ -1071,10 +1204,10 @@ TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) { KeyboardInputMapper& gamepadMapper = constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD); for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) { - process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); + processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD))); - process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); + processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD))); } @@ -1119,22 +1252,22 @@ TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_Alphabetic addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY); addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0); ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0); ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); } @@ -1145,16 +1278,16 @@ TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlph addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY); addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1); ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0); ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); } @@ -1169,22 +1302,22 @@ TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehav mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, AINPUT_SOURCE_KEYBOARD); - std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_DOWN, 1); ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_DOWN, 0); ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); - argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0); ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); } diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index d8c5eac3c4..b7ca24bb61 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -19,10 +19,13 @@ #include "NotifyArgsBuilders.h" #include "android/input.h" +#include <vector> + #include <android-base/logging.h> #include <android-base/properties.h> #include <binder/Binder.h> #include <gtest/gtest.h> +#include <input/InputDevice.h> #include <input/PrintTools.h> #include <inttypes.h> #include <linux/input.h> @@ -51,11 +54,6 @@ static InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t produc return info; } -void setDefaultInputDeviceInfo(LatencyTracker& tracker) { - InputDeviceInfo deviceInfo = generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID); - tracker.setInputDevices({deviceInfo}); -} - const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200); const auto FIRST_MOUSE_POINTER = PointerBuilder(/*id=*/1, ToolType::MOUSE); @@ -120,13 +118,14 @@ protected: std::unique_ptr<LatencyTracker> mTracker; sp<IBinder> connection1; sp<IBinder> connection2; + std::vector<InputDeviceInfo> inputDevices; void SetUp() override { connection1 = sp<BBinder>::make(); connection2 = sp<BBinder>::make(); - mTracker = std::make_unique<LatencyTracker>(*this); - setDefaultInputDeviceInfo(*mTracker); + inputDevices.push_back(generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID)); + mTracker = std::make_unique<LatencyTracker>(*this, inputDevices); } void TearDown() override {} @@ -140,6 +139,10 @@ protected: */ void assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines); + void updateInputDevices(const std::vector<InputDeviceInfo>& inputDevicesUpdated) { + inputDevices = inputDevicesUpdated; + } + private: void processTimeline(const InputEventTimeline& timeline) override { mReceivedTimelines.push_back(timeline); @@ -448,7 +451,7 @@ TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline deviceInfo2.addSource(AINPUT_SOURCE_TOUCHSCREEN); deviceInfo2.addSource(AINPUT_SOURCE_STYLUS); - mTracker->setInputDevices({deviceInfo1, deviceInfo2}); + updateInputDevices({deviceInfo1, deviceInfo2}); mTracker->trackListener( MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, inputEventId) diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 1286a3675c..2b469c5645 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -24,6 +24,7 @@ #include "FakePointerController.h" #include "InterfaceMocks.h" #include "NotifyArgsBuilders.h" +#include "ScopedFlagOverride.h" #include "TestEventMatchers.h" #include "TestInputListener.h" @@ -114,6 +115,10 @@ TestPointerChoreographer::TestPointerChoreographer( }) {} class PointerChoreographerTest : public testing::Test { +public: + static constexpr int DENSITY_MEDIUM = 160; + static constexpr int DENSITY_HIGH = 320; + protected: TestInputListener mTestListener; sp<gui::WindowInfosListener> mRegisteredWindowInfoListener; @@ -124,9 +129,6 @@ protected: mInjectedInitialWindowInfos}; void SetUp() override { - // flag overrides - input_flags::hide_pointer_indicators_for_secure_windows(true); - ON_CALL(mMockPolicy, createPointerController).WillByDefault([this](ControllerType type) { std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>(); EXPECT_FALSE(pc->isPointerShown()); @@ -140,6 +142,22 @@ protected: }); } + void setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) { + if (input_flags::connected_displays_cursor()) { + // setDefaultMouseDisplayId is no-op if connected displays are enabled, mouse display is + // set based on primary display of the topology. + // Setting topology with the primary display should have same effect as calling + // setDefaultMouseDisplayId without topology. + // For this reason in tests we mock this behavior by creating topology with a single + // display. + mChoreographer.setDisplayTopology({.primaryDisplayId = displayId, + .graph{{displayId, {}}}, + .displaysDensity = {{displayId, DENSITY_MEDIUM}}}); + } else { + mChoreographer.setDefaultMouseDisplayId(displayId); + } + } + std::shared_ptr<FakePointerController> assertPointerControllerCreated( ControllerType expectedType) { EXPECT_FALSE(mCreatedControllers.empty()) << "No PointerController was created"; @@ -292,7 +310,7 @@ TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMo TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // For a mouse event without a target display, default viewport should be set for // the PointerController. @@ -309,7 +327,7 @@ TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesSetsDefaultMouseViewportForPointerController) { // Set one display as a default mouse display and emit mouse event to create PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -320,7 +338,7 @@ TEST_F(PointerChoreographerTest, // Change default mouse display. Existing PointerController should be removed and a new one // should be created. - mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); + setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); assertPointerControllerRemoved(firstDisplayPc); auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); @@ -329,7 +347,7 @@ TEST_F(PointerChoreographerTest, } TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) { - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, @@ -340,8 +358,19 @@ TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) { assertPointerDisplayIdNotified(DISPLAY_ID); } +TEST_F(PointerChoreographerTest, NoDefaultMouseSetFallbackToDefaultDisplayId) { + mChoreographer.setDisplayViewports(createViewports({ui::LogicalDisplayId::DEFAULT})); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, + ui::LogicalDisplayId::INVALID)}}); + assertPointerControllerCreated(ControllerType::MOUSE); + + assertPointerDisplayIdNotified(ui::LogicalDisplayId::DEFAULT); +} + TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) { - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -354,7 +383,7 @@ TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplay } TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdChanged) { - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, @@ -373,7 +402,7 @@ TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointe mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); // Set one viewport as a default mouse display ID. - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -382,7 +411,7 @@ TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointe assertPointerDisplayIdNotified(DISPLAY_ID); // Set another viewport as a default mouse display ID. The mouse is moved to the other display. - mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); + setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); assertPointerControllerRemoved(firstDisplayPc); assertPointerControllerCreated(ControllerType::MOUSE); @@ -391,7 +420,7 @@ TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointe TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -421,7 +450,7 @@ TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) { TEST_F(PointerChoreographerTest, AbsoluteMouseMovesPointerAndReturnsNewArgs) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -457,7 +486,7 @@ TEST_F(PointerChoreographerTest, AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) { // Add two displays and set one to default. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // Add two devices, one unassociated and the other associated with non-default mouse display. mChoreographer.notifyInputDevicesChanged( @@ -496,7 +525,7 @@ TEST_F(PointerChoreographerTest, TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -543,7 +572,7 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) { TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -562,7 +591,7 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) { TEST_F(PointerChoreographerTest, MultipleMiceConnectionAndRemoval) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // A mouse is connected, and the pointer is shown. mChoreographer.notifyInputDevicesChanged( @@ -599,7 +628,7 @@ TEST_F(PointerChoreographerTest, MultipleMiceConnectionAndRemoval) { TEST_F(PointerChoreographerTest, UnrelatedChangeDoesNotUnfadePointer) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -628,7 +657,7 @@ TEST_F(PointerChoreographerTest, UnrelatedChangeDoesNotUnfadePointer) { TEST_F(PointerChoreographerTest, DisabledMouseConnected) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); InputDeviceInfo mouseDeviceInfo = generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID); // Disable this mouse device. @@ -641,7 +670,7 @@ TEST_F(PointerChoreographerTest, DisabledMouseConnected) { TEST_F(PointerChoreographerTest, MouseDeviceDisableLater) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); InputDeviceInfo mouseDeviceInfo = generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID); @@ -660,7 +689,7 @@ TEST_F(PointerChoreographerTest, MouseDeviceDisableLater) { TEST_F(PointerChoreographerTest, MultipleEnabledAndDisabledMiceConnectionAndRemoval) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); InputDeviceInfo disabledMouseDeviceInfo = generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID); disabledMouseDeviceInfo.setEnabled(false); @@ -1008,6 +1037,34 @@ TEST_F(PointerChoreographerTest, ShowTouchesOverridesUnspecifiedStylusIcon) { pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER); } +TEST_F(PointerChoreographerTest, StylusHoverEnterFadesMouseOnDisplay) { + // Make sure there are PointerControllers for a mouse and a stylus. + mChoreographer.setStylusPointerIconEnabled(true); + setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID), + generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ui::LogicalDisplayId::INVALID) + .build()); + auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(mousePc->isPointerShown()); + + // Start hovering with a stylus. This should fade the mouse cursor. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + ASSERT_FALSE(mousePc->isPointerShown()); +} + using StylusFixtureParam = std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>; @@ -1378,7 +1435,7 @@ TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedTo TEST_F(PointerChoreographerTest, SetsDefaultTouchpadViewportForPointerController) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // For a touchpad event without a target display, default viewport should be set for // the PointerController. @@ -1394,7 +1451,7 @@ TEST_F(PointerChoreographerTest, WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) { // Set one display as a default touchpad display and create PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, @@ -1403,7 +1460,7 @@ TEST_F(PointerChoreographerTest, firstDisplayPc->assertViewportSet(DISPLAY_ID); // Change default mouse display. Existing PointerController should be removed. - mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); + setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); assertPointerControllerRemoved(firstDisplayPc); auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); @@ -1411,7 +1468,7 @@ TEST_F(PointerChoreographerTest, } TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) { - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, @@ -1423,7 +1480,7 @@ TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) { } TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointerDisplayIdChanged) { - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, @@ -1436,7 +1493,7 @@ TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointe } TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedCallsNotifyPointerDisplayIdChanged) { - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, @@ -1456,7 +1513,7 @@ TEST_F(PointerChoreographerTest, mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); // Set one viewport as a default mouse display ID. - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, @@ -1466,7 +1523,7 @@ TEST_F(PointerChoreographerTest, // Set another viewport as a default mouse display ID. ui::LogicalDisplayId::INVALID will be // notified before a touchpad event. - mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); + setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); assertPointerControllerRemoved(firstDisplayPc); assertPointerControllerCreated(ControllerType::MOUSE); @@ -1475,7 +1532,7 @@ TEST_F(PointerChoreographerTest, TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, @@ -1505,7 +1562,7 @@ TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) { TEST_F(PointerChoreographerTest, TouchpadAddsPointerPositionToTheCoords) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, @@ -1582,7 +1639,7 @@ TEST_F(PointerChoreographerTest, AssociatedTouchpadMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) { // Add two displays and set one to default. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // Add two devices, one unassociated and the other associated with non-default mouse display. mChoreographer.notifyInputDevicesChanged( @@ -1623,7 +1680,7 @@ TEST_F(PointerChoreographerTest, TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, @@ -1660,7 +1717,7 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) { TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, @@ -1680,7 +1737,7 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) TEST_F(PointerChoreographerTest, SetsPointerIconForMouse) { // Make sure there is a PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -1696,7 +1753,7 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouse) { TEST_F(PointerChoreographerTest, DoesNotSetMousePointerIconForWrongDisplayId) { // Make sure there is a PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -1713,7 +1770,7 @@ TEST_F(PointerChoreographerTest, DoesNotSetMousePointerIconForWrongDisplayId) { TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForWrongDeviceId) { // Make sure there is a PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -1730,7 +1787,7 @@ TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForWrongDeviceId) { TEST_F(PointerChoreographerTest, SetsCustomPointerIconForMouse) { // Make sure there is a PointerController. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, @@ -1754,7 +1811,7 @@ TEST_F(PointerChoreographerTest, SetsCustomPointerIconForMouse) { TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) { // Make sure there are two PointerControllers on different displays. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID), @@ -1776,6 +1833,89 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) { firstMousePc->assertPointerIconNotSet(); } +TEST_F(PointerChoreographerTest, A11yPointerMotionFilterMouse) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, + ui::LogicalDisplayId::INVALID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + + pc->setPosition(100, 200); + mChoreographer.setAccessibilityPointerMotionFilterEnabled(true); + + EXPECT_CALL(mMockPolicy, + filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}), + testing::Eq(vec2{10.f, 20.f}), + testing::Eq(DISPLAY_ID))) + .Times(1) + .WillOnce(testing::Return(vec2{4, 13})); + + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ui::LogicalDisplayId::INVALID) + .build()); + + // Cursor position is decided by filtered delta, but pointer coord's relative values are kept. + pc->assertPosition(104, 213); + mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID), + WithCursorPosition(104, 213), + WithRelativeMotion(10, 20))); +} + +TEST_F(PointerChoreographerTest, A11yPointerMotionFilterTouchpad) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ui::LogicalDisplayId::INVALID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + + pc->setPosition(100, 200); + mChoreographer.setAccessibilityPointerMotionFilterEnabled(true); + + EXPECT_CALL(mMockPolicy, + filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}), + testing::Eq(vec2{10.f, 20.f}), + testing::Eq(DISPLAY_ID))) + .Times(1) + .WillOnce(testing::Return(vec2{4, 13})); + + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ui::LogicalDisplayId::INVALID) + .build()); + + // Cursor position is decided by filtered delta, but pointer coord's relative values are kept. + pc->assertPosition(104, 213); + mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID), + WithCursorPosition(104, 213), + WithRelativeMotion(10, 20))); +} + +TEST_F(PointerChoreographerTest, A11yPointerMotionFilterNotFilterTouch) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}}); + mChoreographer.setAccessibilityPointerMotionFilterEnabled(true); + + EXPECT_CALL(mMockPolicy, filterPointerMotionForAccessibility).Times(0); + + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(FIRST_TOUCH_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); +} + using SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam = std::tuple<std::string_view /*name*/, uint32_t /*source*/, ControllerType, PointerBuilder, std::function<void(PointerChoreographer&)>, int32_t /*action*/>; @@ -1966,10 +2106,7 @@ TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture, pc->assertSkipScreenshotFlagNotChanged(); } -TEST_F_WITH_FLAGS( - PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, - hide_pointer_indicators_for_secure_windows))) { +TEST_F(PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); // Add a first mouse device @@ -2127,7 +2264,7 @@ TEST_P(StylusTestFixture, SetsPointerIconForMouseAndStylus) { // Make sure there are PointerControllers for a mouse and a stylus. mChoreographer.setStylusPointerIconEnabled(true); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID), @@ -2162,7 +2299,7 @@ TEST_P(StylusTestFixture, SetsPointerIconForMouseAndStylus) { 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); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID), @@ -2216,7 +2353,7 @@ TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerOnDisplay) TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerWhenDeviceConnected) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // Hide the pointer on the display, and then connect the mouse. mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); @@ -2233,7 +2370,7 @@ TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerWhenDeviceC TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForTouchpad) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // Hide the pointer on the display. mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); @@ -2282,7 +2419,7 @@ TEST_P(StylusTestFixture, SetPointerIconVisibilityHidesPointerForStylus) { TEST_F(PointerChoreographerTest, DrawingTabletCanReportMouseEvent) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, @@ -2309,7 +2446,7 @@ TEST_F(PointerChoreographerTest, DrawingTabletCanReportMouseEvent) { TEST_F(PointerChoreographerTest, MultipleDrawingTabletsReportMouseEvents) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // First drawing tablet is added mChoreographer.notifyInputDevicesChanged( @@ -2357,7 +2494,7 @@ TEST_F(PointerChoreographerTest, MultipleDrawingTabletsReportMouseEvents) { TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) { mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + setDefaultMouseDisplayId(DISPLAY_ID); // Mouse and drawing tablet connected mChoreographer.notifyInputDevicesChanged( @@ -2601,15 +2738,29 @@ TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, TestMetaKeyCom metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT); } -using PointerChoreographerDisplayTopologyTestFixtureParam = +class PointerChoreographerDisplayTopologyTests : public PointerChoreographerTest { +protected: + DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height, + ui::Rotation orientation) { + DisplayViewport viewport; + viewport.displayId = displayId; + viewport.logicalRight = width; + viewport.logicalBottom = height; + viewport.orientation = orientation; + return viewport; + } +}; + +using PointerChoreographerDisplayTopologyCursorTestFixtureParam = std::tuple<std::string_view /*name*/, int32_t /*source device*/, ControllerType /*PointerController*/, ToolType /*pointer tool type*/, vec2 /*source position*/, vec2 /*hover move X/Y*/, ui::LogicalDisplayId /*destination display*/, vec2 /*destination position*/>; -class PointerChoreographerDisplayTopologyTestFixture - : public PointerChoreographerTest, - public testing::WithParamInterface<PointerChoreographerDisplayTopologyTestFixtureParam> { +class PointerChoreographerDisplayTopologyCursorTestFixture + : public PointerChoreographerDisplayTopologyTests, + public testing::WithParamInterface< + PointerChoreographerDisplayTopologyCursorTestFixtureParam> { public: static constexpr ui::LogicalDisplayId DISPLAY_CENTER_ID = ui::LogicalDisplayId{10}; static constexpr ui::LogicalDisplayId DISPLAY_TOP_ID = ui::LogicalDisplayId{20}; @@ -2619,13 +2770,6 @@ public: static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60}; static constexpr ui::LogicalDisplayId DISPLAY_HIGH_DENSITY_ID = ui::LogicalDisplayId{70}; - static constexpr int DENSITY_MEDIUM = 160; - static constexpr int DENSITY_HIGH = 320; - - PointerChoreographerDisplayTopologyTestFixture() { - com::android::input::flags::connected_displays_cursor(true); - } - protected: // Note: viewport size is in pixels and offsets in topology are in dp std::vector<DisplayViewport> mViewports{ @@ -2658,34 +2802,24 @@ protected: {DISPLAY_LEFT_ID, DENSITY_MEDIUM}, {DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM}, {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}}; - -private: - DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height, - ui::Rotation orientation) { - DisplayViewport viewport; - viewport.displayId = displayId; - viewport.logicalRight = width; - viewport.logicalBottom = height; - viewport.orientation = orientation; - return viewport; - } }; -TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDisplayTopologyTest) { +TEST_P(PointerChoreographerDisplayTopologyCursorTestFixture, + PointerChoreographerDisplayTopologyTest) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + const auto& [_, device, pointerControllerType, pointerToolType, initialPosition, hoverMove, destinationDisplay, destinationPosition] = GetParam(); mChoreographer.setDisplayViewports(mViewports); - mChoreographer.setDefaultMouseDisplayId( - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID); + setDefaultMouseDisplayId(DISPLAY_CENTER_ID); mChoreographer.setDisplayTopology(mTopology); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, device, ui::LogicalDisplayId::INVALID)}}); auto pc = assertPointerControllerCreated(pointerControllerType); - ASSERT_EQ(PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, - pc->getDisplayId()); + ASSERT_EQ(DISPLAY_CENTER_ID, pc->getDisplayId()); // Set initial position of the PointerController. pc->setPosition(initialPosition.x, initialPosition.y); @@ -2717,92 +2851,320 @@ TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDispl } INSTANTIATE_TEST_SUITE_P( - PointerChoreographerTest, PointerChoreographerDisplayTopologyTestFixture, + PointerChoreographerTest, PointerChoreographerDisplayTopologyCursorTestFixture, testing::Values( // Note: Upon viewport transition cursor will be positioned at the boundary of the // destination, as we drop any unconsumed delta. - std::make_tuple("PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE, - ControllerType::MOUSE, ToolType::MOUSE, - vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, - vec2(50, 50) /* destination x/y */), - std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, - ToolType::MOUSE, vec2(50, 50) /* initial x/y */, - vec2(25, 25) /* delta x/y */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, - vec2(75, 75) /* destination x/y */), - std::make_tuple("TransitionToRightDisplay", AINPUT_SOURCE_MOUSE, - ControllerType::MOUSE, ToolType::MOUSE, - vec2(50, 50) /* initial x/y */, vec2(100, 25) /* delta x/y */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_RIGHT_ID, - vec2(0, - 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */), + std::make_tuple( + "PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, + ToolType::MOUSE, vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID, + vec2(50, 50) /* destination x/y */), + std::make_tuple( + "UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, + ToolType::MOUSE, vec2(50, 50) /* initial x/y */, + vec2(25, 25) /* delta x/y */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID, + vec2(75, 75) /* destination x/y */), + std::make_tuple( + "TransitionToRightDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, + ToolType::MOUSE, vec2(50, 50) /* initial x/y */, + vec2(100, 25) /* delta x/y */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_RIGHT_ID, + vec2(0, 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */), std::make_tuple( "TransitionToLeftDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, ToolType::MOUSE, vec2(50, 50) /* initial x/y */, vec2(-100, 25) /* delta x/y */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_LEFT_ID, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_LEFT_ID, vec2(90, 50 + 25 - 10) /* Right edge: (width, source + delta - offset*/), - std::make_tuple("TransitionToTopDisplay", - AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE, - ToolType::FINGER, vec2(50, 50) /* initial x/y */, - vec2(25, -100) /* delta x/y */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID, - vec2(50 + 25 - 50, - 90) /* Bottom edge: (source + delta - offset, height) */), - std::make_tuple("TransitionToBottomDisplay", - AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE, - ToolType::FINGER, vec2(50, 50) /* initial x/y */, - vec2(25, 100) /* delta x/y */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID, - vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */), + std::make_tuple( + "TransitionToTopDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ControllerType::MOUSE, ToolType::FINGER, vec2(50, 50) /* initial x/y */, + vec2(25, -100) /* delta x/y */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_TOP_ID, + vec2(50 + 25 - 50, + 90) /* Bottom edge: (source + delta - offset, height) */), + std::make_tuple( + "TransitionToBottomDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ControllerType::MOUSE, ToolType::FINGER, vec2(50, 50) /* initial x/y */, + vec2(25, 100) /* delta x/y */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_BOTTOM_ID, + vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */), // move towards 25 dp gap between DISPLAY_HIGH_DENSITY_ID and DISPLAY_TOP_ID - std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE, - ControllerType::MOUSE, ToolType::MOUSE, - vec2(35, 50) /* initial x/y */, vec2(0, -100) /* Move Up */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, - vec2(35, 0) /* Top edge */), - std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE, - ControllerType::MOUSE, ToolType::MOUSE, - vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, - vec2(99, 5) /* Top edge */), - std::make_tuple("NoTransitionAtBottomOffset", - AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE, - ToolType::FINGER, vec2(5, 95) /* initial x/y */, - vec2(0, 100) /* Move Down */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, - vec2(5, 99) /* Bottom edge */), - std::make_tuple("NoTransitionAtLeftOffset", - AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE, - ToolType::FINGER, vec2(5, 5) /* initial x/y */, - vec2(-100, 0) /* Move Left */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, - vec2(0, 5) /* Left edge */), std::make_tuple( - "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, - ControllerType::MOUSE, ToolType::FINGER, vec2(95, 5) /* initial x/y */, - vec2(10, -10) /* Move diagonally to top right corner */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID, - vec2(0, 90) /* bottom left corner */), + "NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, + ToolType::MOUSE, vec2(35, 50) /* initial x/y */, + vec2(0, -100) /* Move Up */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID, + vec2(35, 0) /* Top edge */), std::make_tuple( - "TransitionToHighDpDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, - ControllerType::MOUSE, ToolType::MOUSE, vec2(20, 20) /* initial x/y */, - vec2(0, -50) /* delta x/y */, - PointerChoreographerDisplayTopologyTestFixture::DISPLAY_HIGH_DENSITY_ID, - /* Bottom edge: ((source + delta - offset) * density, height) */ - vec2((20 + 0 + 75) * 2, 200))), - [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) { - return std::string{std::get<0>(p.param)}; - }); + "NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, + ToolType::MOUSE, vec2(95, 5) /* initial x/y */, + vec2(100, 0) /* Move Right */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID, + vec2(99, 5) /* Top edge */), + std::make_tuple( + "NoTransitionAtBottomOffset", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ControllerType::MOUSE, ToolType::FINGER, vec2(5, 95) /* initial x/y */, + vec2(0, 100) /* Move Down */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID, + vec2(5, 99) /* Bottom edge */), + std::make_tuple( + "NoTransitionAtLeftOffset", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ControllerType::MOUSE, ToolType::FINGER, vec2(5, 5) /* initial x/y */, + vec2(-100, 0) /* Move Left */, + PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID, + vec2(0, 5) /* Left edge */), + std::make_tuple("TransitionAtTopRightCorner", + AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE, + ToolType::FINGER, vec2(95, 5) /* initial x/y */, + vec2(10, -10) /* Move diagonally to top right corner */, + PointerChoreographerDisplayTopologyCursorTestFixture:: + DISPLAY_TOP_RIGHT_CORNER_ID, + vec2(0, 90) /* bottom left corner */), + std::make_tuple("TransitionToHighDpDisplay", + AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE, + ToolType::MOUSE, vec2(20, 20) /* initial x/y */, + vec2(0, -50) /* delta x/y */, + PointerChoreographerDisplayTopologyCursorTestFixture:: + DISPLAY_HIGH_DENSITY_ID, + /* Bottom edge: ((source + delta - offset) * density, height) */ + vec2((20 + 0 + 75) * 2, 200))), + [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyCursorTestFixtureParam>& + p) { return std::string{std::get<0>(p.param)}; }); + +class PointerChoreographerDisplayTopologyDefaultMouseDisplayTests + : public PointerChoreographerDisplayTopologyTests { +protected: + static constexpr ui::LogicalDisplayId FIRST_DISPLAY_ID = ui::LogicalDisplayId{10}; + static constexpr ui::LogicalDisplayId SECOND_DISPLAY_ID = ui::LogicalDisplayId{20}; + static constexpr ui::LogicalDisplayId THIRD_DISPLAY_ID = ui::LogicalDisplayId{30}; + + DisplayViewport createViewport(ui::LogicalDisplayId displayId) { + return PointerChoreographerDisplayTopologyTests::createViewport(displayId, /*width=*/100, + /*height=*/100, + ui::ROTATION_0); + } + + void setDisplayTopologyWithDisplays( + ui::LogicalDisplayId primaryDisplayId, + const std::vector<ui::LogicalDisplayId>& adjacentDisplays = {}) { + // Prepare a topology with all display connected from left to right. + ui::LogicalDisplayId previousDisplay = primaryDisplayId; + + std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> + topologyGraph; + topologyGraph[primaryDisplayId] = {}; + + std::unordered_map<ui::LogicalDisplayId, int> displaysDensity; + displaysDensity[primaryDisplayId] = DENSITY_MEDIUM; + + for (ui::LogicalDisplayId adjacentDisplayId : adjacentDisplays) { + topologyGraph[previousDisplay].push_back({.displayId = adjacentDisplayId, + .position = DisplayTopologyPosition::RIGHT, + .offsetDp = 0.0f}); + topologyGraph[adjacentDisplayId].push_back({.displayId = previousDisplay, + .position = DisplayTopologyPosition::LEFT, + .offsetDp = 0.0f}); + + displaysDensity[adjacentDisplayId] = DENSITY_MEDIUM; + } + + mChoreographer.setDisplayTopology({primaryDisplayId, topologyGraph, displaysDensity}); + } +}; + +TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests, + UnrelatedTopologyUpdatesDoNotChangeCursorDisplay) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + + // Set first display as primary display and emit mouse event to create PointerController. + mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID)}); + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, + ui::LogicalDisplayId::INVALID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(FIRST_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); + + // Add another display keeping the primary display unchanged + mChoreographer.setDisplayViewports( + {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)}); + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID, + /*adjacentDisplays=*/{SECOND_DISPLAY_ID}); + + assertPointerControllerNotCreated(); + pc->assertViewportSet(FIRST_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); + + // Move cursor to second display and add a third display + auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE) + .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100) + .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(pointerBuilder) + .deviceId(DEVICE_ID) + .displayId(ui::LogicalDisplayId::INVALID) + .build()); + + assertPointerControllerNotCreated(); + pc->assertViewportSet(SECOND_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); + + mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID), + createViewport(SECOND_DISPLAY_ID), + createViewport(THIRD_DISPLAY_ID)}); + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID, /*adjacentDisplays=*/ + {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID}); + + assertPointerControllerNotCreated(); + pc->assertViewportSet(SECOND_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); + + // Change the primary display to the third display + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/THIRD_DISPLAY_ID, /*adjacentDisplays=*/ + {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID}); + + assertPointerControllerNotCreated(); + pc->assertViewportSet(SECOND_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); +} + +TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests, + PrimaryDisplayIsFallbackOnPointerDisplayRemoved) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + + // Add two displays and move cursor to the secondary display + mChoreographer.setDisplayViewports( + {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)}); + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID, + /*adjacentDisplays=*/{SECOND_DISPLAY_ID}); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, + ui::LogicalDisplayId::INVALID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(FIRST_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); + + auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE) + .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100) + .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(pointerBuilder) + .deviceId(DEVICE_ID) + .displayId(ui::LogicalDisplayId::INVALID) + .build()); + + assertPointerControllerNotCreated(); + pc->assertViewportSet(SECOND_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); + + // Remove the secondary display + mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID)}); + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID); + + assertPointerControllerRemoved(pc); + pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(FIRST_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); +} + +TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests, + UsePrimaryDisplayIfAssociatedDisplayIsInTopology) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true); + + // Add two displays + mChoreographer.setDisplayViewports( + {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)}); + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/SECOND_DISPLAY_ID, + /*adjacentDisplays=*/{FIRST_DISPLAY_ID}); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, FIRST_DISPLAY_ID)}}); + + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(SECOND_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); +} + +TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests, + AllowCrossingDisplayEvenWithAssociatedDisplaySet) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true); + + // Add two displays + mChoreographer.setDisplayViewports( + {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)}); + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID, + /*adjacentDisplays=*/{SECOND_DISPLAY_ID}); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)}}); + + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(FIRST_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); + + // Move cursor to the secondary display + auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE) + .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100) + .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(pointerBuilder) + .deviceId(DEVICE_ID) + .displayId(ui::LogicalDisplayId::INVALID) + .build()); + + assertPointerControllerNotCreated(); + pc->assertViewportSet(SECOND_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); +} + +TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests, + AddAssociatedDisplayCursorOutsideOfDisplayTopology) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true); + + // Add three displays, with only first and second display in DisplayTopolgoy + mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID), + createViewport(SECOND_DISPLAY_ID), + createViewport(THIRD_DISPLAY_ID)}); + setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID, + /*adjacentDisplays=*/{SECOND_DISPLAY_ID}); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, + ui::LogicalDisplayId::INVALID)}}); + + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(FIRST_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); + + // Adds a new mouse associated with third display + mChoreographer.notifyInputDevicesChanged( + {/*id=*/1, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, THIRD_DISPLAY_ID)}}); + + pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(THIRD_DISPLAY_ID); + ASSERT_TRUE(pc->isPointerShown()); +} class PointerChoreographerWindowInfoListenerTest : public testing::Test {}; -TEST_F_WITH_FLAGS( - PointerChoreographerWindowInfoListenerTest, - doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, - hide_pointer_indicators_for_secure_windows))) { +TEST_F(PointerChoreographerWindowInfoListenerTest, + doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed) { sp<android::gui::WindowInfosListener> registeredListener; sp<android::gui::WindowInfosListener> localListenerCopy; { diff --git a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h index 767f9cdfa7..43975f0836 100644 --- a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h +++ b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h @@ -21,6 +21,14 @@ namespace android { static constexpr int32_t MAX_RANDOM_POINTERS = 4; static constexpr int32_t MAX_RANDOM_DEVICES = 4; +// The maximum value that we use for the action button field of NotifyMotionArgs. (We allow multiple +// bits to be set for this since we're just trying to generate a fuzzed event stream that doesn't +// cause crashes when enum values are converted to Rust — we don't necessarily want it to be valid.) +// +// AMOTION_EVENT_BUTTON_STYLUS_SECONDARY should be replaced with whatever AMOTION_EVENT_BUTTON_ +// value is highest if the enum is edited. +static constexpr int8_t MAX_ACTION_BUTTON_VALUE = (AMOTION_EVENT_BUTTON_STYLUS_SECONDARY << 1) - 1; + int getFuzzedMotionAction(FuzzedDataProvider& fdp) { int actionMasked = fdp.PickValueInArray<int>({ AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE, @@ -185,18 +193,16 @@ NotifyMotionArgs generateFuzzedMotionArgs(IdGenerator& idGenerator, FuzzedDataPr fdp.ConsumeIntegralInRange<nsecs_t>(currentTime - 5E9, currentTime + 5E9); const nsecs_t readTime = downTime; const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(downTime, downTime + 1E9); + const int32_t actionButton = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_ACTION_BUTTON_VALUE); const float cursorX = fdp.ConsumeIntegralInRange<int>(-10000, 10000); const float cursorY = fdp.ConsumeIntegralInRange<int>(-10000, 10000); return NotifyMotionArgs(idGenerator.nextId(), eventTime, readTime, deviceId, source, displayId, - POLICY_FLAG_PASS_TO_USER, action, - /*actionButton=*/fdp.ConsumeIntegral<int32_t>(), + POLICY_FLAG_PASS_TO_USER, action, actionButton, getFuzzedFlags(fdp, action), AMETA_NONE, getFuzzedButtonState(fdp), MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, - pointerProperties.data(), pointerCoords.data(), - /*xPrecision=*/0, - /*yPrecision=*/0, cursorX, cursorY, downTime, - /*videoFrames=*/{}); + pointerProperties.data(), pointerCoords.data(), /*xPrecision=*/0, + /*yPrecision=*/0, cursorX, cursorY, downTime, /*videoFrames=*/{}); } } // namespace android diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp index 157a3338da..9c027fa00c 100644 --- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -17,6 +17,8 @@ #include <fuzzer/FuzzedDataProvider.h> #include <linux/input.h> +#include <vector> + #include "../../InputDeviceMetricsSource.h" #include "../InputEventTimeline.h" #include "NotifyArgsBuilders.h" @@ -58,7 +60,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { FuzzedDataProvider fdp(data, size); EmptyProcessor emptyProcessor; - LatencyTracker tracker(emptyProcessor); + std::vector<InputDeviceInfo> emptyDevices; + LatencyTracker tracker(emptyProcessor, emptyDevices); // Make some pre-defined tokens to ensure that some timelines are complete. std::array<sp<IBinder> /*token*/, 10> predefinedTokens; diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 846260a946..9a5903981b 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -34,6 +34,28 @@ constexpr size_t kValidTypes[] = {EV_SW, android::EventHubInterface::DEVICE_ADDED, android::EventHubInterface::DEVICE_REMOVED}; +static const android::InputDeviceClass kInputDeviceClasses[] = { + android::InputDeviceClass::KEYBOARD, + android::InputDeviceClass::ALPHAKEY, + android::InputDeviceClass::TOUCH, + android::InputDeviceClass::CURSOR, + android::InputDeviceClass::TOUCH_MT, + android::InputDeviceClass::DPAD, + android::InputDeviceClass::GAMEPAD, + android::InputDeviceClass::SWITCH, + android::InputDeviceClass::JOYSTICK, + android::InputDeviceClass::VIBRATOR, + android::InputDeviceClass::MIC, + android::InputDeviceClass::EXTERNAL_STYLUS, + android::InputDeviceClass::ROTARY_ENCODER, + android::InputDeviceClass::SENSOR, + android::InputDeviceClass::BATTERY, + android::InputDeviceClass::LIGHT, + android::InputDeviceClass::TOUCHPAD, + android::InputDeviceClass::VIRTUAL, + android::InputDeviceClass::EXTERNAL, +}; + constexpr size_t kValidCodes[] = { SYN_REPORT, ABS_MT_SLOT, @@ -105,7 +127,13 @@ public: void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); } ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override { - return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>()); + uint32_t flags = 0; + for (auto inputDeviceClass : kInputDeviceClasses) { + if (mFdp->ConsumeBool()) { + flags |= static_cast<uint32_t>(inputDeviceClass); + } + } + return ftl::Flags<InputDeviceClass>(flags); } InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override { return mIdentifier; @@ -332,6 +360,7 @@ public: std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) : mEventHub(eventHub), mPolicy(sp<FuzzInputReaderPolicy>::make(fdp)), mFdp(fdp) {} ~FuzzInputReaderContext() {} + std::string dump() { return "(dump from FuzzInputReaderContext)"; } void updateGlobalMetaState() override {} int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); } void disableVirtualKeysUntil(nsecs_t time) override {} @@ -367,8 +396,8 @@ private: template <class Fdp> InputDevice getFuzzedInputDevice(Fdp& fdp, FuzzInputReaderContext* context) { InputDeviceIdentifier identifier; - identifier.name = fdp.ConsumeRandomLengthString(16); - identifier.location = fdp.ConsumeRandomLengthString(12); + identifier.name = fdp.ConsumeRandomLengthUtf8String(16); + identifier.location = fdp.ConsumeRandomLengthUtf8String(12); int32_t deviceID = fdp.ConsumeIntegralInRange(0, 5); int32_t deviceGeneration = fdp.ConsumeIntegralInRange(0, 5); return InputDevice(context, deviceID, deviceGeneration, identifier); diff --git a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h index 2f76f181cb..b2581184e8 100644 --- a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h +++ b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h @@ -15,7 +15,7 @@ */ #include <fuzzer/FuzzedDataProvider.h> - +#include <algorithm> /** * A thread-safe interface to the FuzzedDataProvider */ @@ -60,6 +60,44 @@ public: return FuzzedDataProvider::ConsumeRandomLengthString(); } + // Converting the string to a UTF-8 string by setting the prefix bits of each + // byte according to UTF-8 encoding rules. + std::string ConsumeRandomLengthUtf8String(size_t max_length) { + std::scoped_lock _l(mLock); + std::string result = FuzzedDataProvider::ConsumeRandomLengthString(max_length); + size_t remaining_bytes = result.length(), idx = 0; + while (remaining_bytes > 0) { + size_t random_byte_size = FuzzedDataProvider::ConsumeIntegralInRange(1, 4); + size_t byte_size = std::min(random_byte_size, remaining_bytes); + switch (byte_size) { + // Prefix byte: 0xxxxxxx + case 1: + result[idx++] &= 0b01111111; + break; + // Prefix bytes: 110xxxxx 10xxxxxx + case 2: + result[idx++] = (result[idx] & 0b00011111) | 0b11000000; + result[idx++] = (result[idx] & 0b00111111) | 0b10000000; + break; + // Prefix bytes: 1110xxxx 10xxxxxx 10xxxxxx + case 3: + result[idx++] = (result[idx] & 0b00001111) | 0b11100000; + result[idx++] = (result[idx] & 0b00111111) | 0b10000000; + result[idx++] = (result[idx] & 0b00111111) | 0b10000000; + break; + // Prefix bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + case 4: + result[idx++] = (result[idx] & 0b00000111) | 0b11110000; + result[idx++] = (result[idx] & 0b00111111) | 0b10000000; + result[idx++] = (result[idx] & 0b00111111) | 0b10000000; + result[idx++] = (result[idx] & 0b00111111) | 0b10000000; + break; + } + remaining_bytes -= byte_size; + } + return result; + } + std::string ConsumeRemainingBytesAsString() { std::scoped_lock _l(mLock); return FuzzedDataProvider::ConsumeRemainingBytesAsString(); diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 4f65e77462..78f8f7bd96 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -52,6 +52,7 @@ cc_library_shared { ], whole_static_libs: [ + "android.adpf.sessionmanager_aidl-ndk", "android.os.hintmanager_aidl-ndk", ], diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp index 0ba1909a44..a817a7bf2e 100644 --- a/services/powermanager/PowerHalController.cpp +++ b/services/powermanager/PowerHalController.cpp @@ -173,6 +173,21 @@ HalResult<aidl::android::hardware::power::SupportInfo> PowerHalController::getSu return CACHE_SUPPORT(6, processHalResult(handle->getSupportInfo(), "getSupportInfo")); } +HalResult<void> PowerHalController::sendCompositionData( + const std::vector<hal::CompositionData>& data) { + std::shared_ptr<HalWrapper> handle = initHal(); + return CACHE_SUPPORT(6, + processHalResult(handle->sendCompositionData(data), + "sendCompositionData")); +} + +HalResult<void> PowerHalController::sendCompositionUpdate(const hal::CompositionUpdate& update) { + std::shared_ptr<HalWrapper> handle = initHal(); + return CACHE_SUPPORT(6, + processHalResult(handle->sendCompositionUpdate(update), + "sendCompositionUpdate")); +} + } // namespace power } // namespace android diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp index 068c23f94a..9c83bf5af3 100644 --- a/services/powermanager/PowerHalWrapper.cpp +++ b/services/powermanager/PowerHalWrapper.cpp @@ -79,6 +79,16 @@ HalResult<Aidl::SupportInfo> EmptyHalWrapper::getSupportInfo() { return HalResult<Aidl::SupportInfo>::unsupported(); } +HalResult<void> EmptyHalWrapper::sendCompositionData(const std::vector<hal::CompositionData>&) { + ALOGV("Skipped sendCompositionData because %s", getUnsupportedMessage()); + return HalResult<void>::unsupported(); +} + +HalResult<void> EmptyHalWrapper::sendCompositionUpdate(const hal::CompositionUpdate&) { + ALOGV("Skipped sendCompositionUpdate because %s", getUnsupportedMessage()); + return HalResult<void>::unsupported(); +} + const char* EmptyHalWrapper::getUnsupportedMessage() { return "Power HAL is not supported"; } @@ -292,6 +302,14 @@ HalResult<Aidl::SupportInfo> AidlHalWrapper::getSupportInfo() { return HalResult<Aidl::SupportInfo>::fromStatus(result, std::move(support)); } +HalResult<void> AidlHalWrapper::sendCompositionData(const std::vector<hal::CompositionData>& data) { + return HalResult<void>::fromStatus(mHandle->sendCompositionData(data)); +} + +HalResult<void> AidlHalWrapper::sendCompositionUpdate(const hal::CompositionUpdate& update) { + return HalResult<void>::fromStatus(mHandle->sendCompositionUpdate(update)); +} + const char* AidlHalWrapper::getUnsupportedMessage() { return "Power HAL doesn't support it"; } diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index 682d1f44dc..1c53496644 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -26,9 +26,9 @@ #include <utils/Log.h> #include <unistd.h> +#include <memory> #include <thread> - using android::binder::Status; using namespace aidl::android::hardware::power; @@ -347,3 +347,50 @@ TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionWithConfigUnsupported) { result = mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out); ASSERT_TRUE(result.isUnsupported()); } + +TEST_F(PowerHalWrapperAidlTest, TestSendingCompositionData) { + int32_t tgid = 999; + int32_t uid = 1001; + std::vector<hal::CompositionData> dataOut; + dataOut.emplace_back(hal::CompositionData{ + .timestampNanos = 0L, + .scheduledPresentTimestampsNanos = {100}, + .latchTimestampNanos = 50, + .outputIds = {0}, + }); + dataOut.emplace_back(hal::CompositionData{ + .timestampNanos = 200L, + .scheduledPresentTimestampsNanos = {300}, + .latchTimestampNanos = 250, + .outputIds = {0}, + }); + EXPECT_CALL(*mMockHal.get(), sendCompositionData(_)) + .Times(Exactly(1)) + .WillOnce([&](const std::vector<hal::CompositionData>& passedData) { + if (!std::equal(passedData.begin(), passedData.end(), dataOut.begin())) { + ADD_FAILURE() << "Passed composition data not the same"; + } + return ndk::ScopedAStatus::ok(); + }); + + ASSERT_TRUE(mWrapper->sendCompositionData(dataOut).isOk()); +} + +TEST_F(PowerHalWrapperAidlTest, TestSendingCompositionUpdate) { + int32_t tgid = 999; + int32_t uid = 1001; + hal::CompositionUpdate dataOut{ + .timestampNanos = 123, + .deadOutputIds = {1, 2, 3}, + }; + EXPECT_CALL(*mMockHal.get(), sendCompositionUpdate(_)) + .Times(Exactly(1)) + .WillOnce([&](const hal::CompositionUpdate& passedData) { + if (passedData != dataOut) { + ADD_FAILURE() << "Passed composition update data not the same"; + } + return ndk::ScopedAStatus::ok(); + }); + + ASSERT_TRUE(mWrapper->sendCompositionUpdate(dataOut).isOk()); +}
\ No newline at end of file diff --git a/services/sensorservice/sensorservice_flags.aconfig b/services/sensorservice/sensorservice_flags.aconfig index 452b42826c..63089737e3 100644 --- a/services/sensorservice/sensorservice_flags.aconfig +++ b/services/sensorservice/sensorservice_flags.aconfig @@ -37,4 +37,14 @@ flag { metadata { purpose: PURPOSE_BUGFIX } +} + +flag { + name: "disable_vndk_forged_name" + namespace: "sensors" + description: "When this flag is enabled, sensor manager will not use forged name to determine if an access is from VNDK" + bug: "398253250" + metadata { + purpose: PURPOSE_BUGFIX + } }
\ No newline at end of file diff --git a/services/stats/Android.bp b/services/stats/Android.bp index f698515545..d588017737 100644 --- a/services/stats/Android.bp +++ b/services/stats/Android.bp @@ -29,10 +29,15 @@ cc_library_shared { "libexpresslog", "libhidlbase", "liblog", - "libstatslog", "libstatssocket", "libutils", ], + generated_sources: [ + "statslog_hidl.cpp", + ], + generated_headers: [ + "statslog_hidl.h", + ], export_include_dirs: [ "include/", ], @@ -47,3 +52,28 @@ cc_library_shared { "android.frameworks.stats-service.xml", ], } + +genrule { + name: "statslog_hidl.h", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen)" + + " --header $(genDir)/statslog_hidl.h" + + " --module statshidl" + + " --namespace android,util,statshidl", + out: [ + "statslog_hidl.h", + ], +} + +genrule { + name: "statslog_hidl.cpp", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen)" + + " --cpp $(genDir)/statslog_hidl.cpp" + + " --module statshidl" + + " --namespace android,util,statshidl" + + " --importHeader statslog_hidl.h", + out: [ + "statslog_hidl.cpp", + ], +} diff --git a/services/stats/OWNERS b/services/stats/OWNERS index a61babf32e..791b711de8 100644 --- a/services/stats/OWNERS +++ b/services/stats/OWNERS @@ -1,9 +1,7 @@ jeffreyhuang@google.com joeo@google.com -jtnguyen@google.com muhammadq@google.com ruchirr@google.com singhtejinder@google.com tsaichristine@google.com yaochen@google.com -yro@google.com diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp index b22f903654..66f7682dd8 100644 --- a/services/stats/StatsAidl.cpp +++ b/services/stats/StatsAidl.cpp @@ -26,9 +26,8 @@ #include <log/log.h> #include <stats_annotations.h> #include <stats_event.h> -#include <statslog.h> -#include <unordered_map> +#include <map> namespace { static const char* g_AtomErrorMetricName = @@ -118,8 +117,8 @@ ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { } } - // populate map for quickier access for VendorAtomValue associated annotations by value index - std::unordered_map<int, int> fieldIndexToAnnotationSetMap; + // populate map for quicker access for VendorAtomValue associated annotations by value index + std::map<int, int> fieldIndexToAnnotationSetMap; if (vendorAtom.valuesAnnotations) { const std::vector<std::optional<AnnotationSet>>& valuesAnnotations = *vendorAtom.valuesAnnotations; diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp index 19176d9aaf..0ffa4c3947 100644 --- a/services/stats/StatsHal.cpp +++ b/services/stats/StatsHal.cpp @@ -20,7 +20,7 @@ #include "StatsHal.h" #include <log/log.h> -#include <statslog.h> +#include <statslog_hidl.h> namespace android { namespace frameworks { @@ -32,24 +32,27 @@ StatsHal::StatsHal() { } hardware::Return<void> StatsHal::reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) { - android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED, - speakerImpedance.speakerLocation, speakerImpedance.milliOhms); + android::util::statshidl::stats_write(android::util::statshidl::SPEAKER_IMPEDANCE_REPORTED, + speakerImpedance.speakerLocation, + speakerImpedance.milliOhms); return hardware::Void(); } hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) { - android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType), - hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode)); + android::util::statshidl::stats_write( + android::util::statshidl::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType), + hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode)); return hardware::Void(); } hardware::Return<void> StatsHal::reportPhysicalDropDetected( const PhysicalDropDetected& physicalDropDetected) { - android::util::stats_write( - android::util::PHYSICAL_DROP_DETECTED, int32_t(physicalDropDetected.confidencePctg), - physicalDropDetected.accelPeak, physicalDropDetected.freefallDuration); + android::util::statshidl::stats_write(android::util::statshidl::PHYSICAL_DROP_DETECTED, + int32_t(physicalDropDetected.confidencePctg), + physicalDropDetected.accelPeak, + physicalDropDetected.freefallDuration); return hardware::Void(); } @@ -60,19 +63,19 @@ hardware::Return<void> StatsHal::reportChargeCycles(const ChargeCycles& chargeCy for (int i = 0; i < 10 - initialSize; i++) { buckets.push_back(0); // Push 0 for buckets that do not exist. } - android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], - buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], - buckets[7], buckets[8], buckets[9]); + android::util::statshidl::stats_write( + android::util::statshidl::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], buckets[2], + buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], buckets[9]); return hardware::Void(); } hardware::Return<void> StatsHal::reportBatteryHealthSnapshot( const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) { - android::util::stats_write( - android::util::BATTERY_HEALTH_SNAPSHOT, int32_t(batteryHealthSnapshotArgs.type), - batteryHealthSnapshotArgs.temperatureDeciC, batteryHealthSnapshotArgs.voltageMicroV, - batteryHealthSnapshotArgs.currentMicroA, + android::util::statshidl::stats_write( + android::util::statshidl::BATTERY_HEALTH_SNAPSHOT, + int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC, + batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA, batteryHealthSnapshotArgs.openCircuitVoltageMicroV, batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent); @@ -80,23 +83,24 @@ hardware::Return<void> StatsHal::reportBatteryHealthSnapshot( } hardware::Return<void> StatsHal::reportSlowIo(const SlowIo& slowIo) { - android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count); + android::util::statshidl::stats_write(android::util::statshidl::SLOW_IO, + int32_t(slowIo.operation), slowIo.count); return hardware::Void(); } hardware::Return<void> StatsHal::reportBatteryCausedShutdown( const BatteryCausedShutdown& batteryCausedShutdown) { - android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN, - batteryCausedShutdown.voltageMicroV); + android::util::statshidl::stats_write(android::util::statshidl::BATTERY_CAUSED_SHUTDOWN, + batteryCausedShutdown.voltageMicroV); return hardware::Void(); } hardware::Return<void> StatsHal::reportUsbPortOverheatEvent( const UsbPortOverheatEvent& usbPortOverheatEvent) { - android::util::stats_write( - android::util::USB_PORT_OVERHEAT_EVENT_REPORTED, + android::util::statshidl::stats_write( + android::util::statshidl::USB_PORT_OVERHEAT_EVENT_REPORTED, usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC, usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis, usbPortOverheatEvent.timeToInactive); @@ -105,9 +109,10 @@ hardware::Return<void> StatsHal::reportUsbPortOverheatEvent( } hardware::Return<void> StatsHal::reportSpeechDspStat(const SpeechDspStat& speechDspStat) { - android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED, - speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis, - speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount); + android::util::statshidl::stats_write( + android::util::statshidl::SPEECH_DSP_STAT_REPORTED, speechDspStat.totalUptimeMillis, + speechDspStat.totalDowntimeMillis, speechDspStat.totalCrashCount, + speechDspStat.totalRecoverCount); return hardware::Void(); } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h index 252adaa8e3..2c0a66fac3 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h @@ -34,7 +34,7 @@ class CompositionEngine; * A parameter object for creating Display instances */ struct DisplayCreationArgs { - DisplayId id; + DisplayIdVariant idVariant; // Size of the display in pixels ui::Size pixels = ui::kInvalidSize; @@ -68,8 +68,8 @@ class DisplayCreationArgsBuilder { public: DisplayCreationArgs build() { return std::move(mArgs); } - DisplayCreationArgsBuilder& setId(DisplayId id) { - mArgs.id = id; + DisplayCreationArgsBuilder& setId(DisplayIdVariant idVariant) { + mArgs.idVariant = idVariant; return *this; } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index 780758e2a3..e2ea0f1397 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -167,6 +167,8 @@ public: // Checks if the buffer's release fence has been set virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0; + virtual void setReleasedBuffer(sp<GraphicBuffer> buffer) = 0; + // Indicates that the picture profile request was applied to this layer. virtual void onPictureProfileCommitted() = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index bda7856596..4266da4b07 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -170,6 +170,7 @@ public: // Returns the DisplayId the output represents, if it has one virtual ftl::Optional<DisplayId> getDisplayId() const = 0; + virtual ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const = 0; // Enables (or disables) composition on this output virtual void setCompositionEnabled(bool) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index c1b864df02..c0243b81db 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -145,7 +145,7 @@ public: // Applies a HWC device layer lut virtual void applyDeviceLayerLut( - ndk::ScopedFileDescriptor, + ::android::base::unique_fd, std::vector<std::pair< int, aidl::android::hardware::graphics::composer3::LutProperties>>) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index 5519aafe11..ec87acc372 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -46,6 +46,7 @@ public: // compositionengine::Output overrides ftl::Optional<DisplayId> getDisplayId() const override; + ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override; bool isValid() const override; void dump(std::string&) const override; using compositionengine::impl::Output::setReleasedLayers; @@ -104,8 +105,11 @@ private: override; bool hasPictureProcessing() const override; int32_t getMaxLayerPictureProfiles() const override; + bool isGpuVirtualDisplay() const { + return std::holds_alternative<GpuVirtualDisplayId>(mIdVariant); + } - DisplayId mId; + DisplayIdVariant mIdVariant; bool mIsDisconnected = false; adpf::PowerAdvisor* mPowerAdvisor = nullptr; bool mHasPictureProcessing = false; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 0ccdd22919..873764b065 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -45,6 +45,7 @@ public: // compositionengine::Output overrides bool isValid() const override; ftl::Optional<DisplayId> getDisplayId() const override; + ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override; void setCompositionEnabled(bool) override; void setLayerCachingEnabled(bool) override; void setLayerCachingTexturePoolEnabled(bool) override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index a1434f21cc..efddc857cd 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -68,7 +68,7 @@ public: aidl::android::hardware::graphics::composer3::Composition) override; void prepareForDeviceLayerRequests() override; void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override; - void applyDeviceLayerLut(ndk::ScopedFileDescriptor, + void applyDeviceLayerLut(::android::base::unique_fd, std::vector<std::pair<int, LutProperties>>) override; bool needsFiltering() const override; std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override; @@ -104,7 +104,7 @@ private: void detectDisallowedCompositionTypeChange( aidl::android::hardware::graphics::composer3::Composition from, aidl::android::hardware::graphics::composer3::Composition to) const; - bool isClientCompositionForced(bool isPeekingThrough, bool isCached) const; + bool isClientCompositionForced(bool isPeekingThrough) const; void updateLuts(const LayerFECompositionState&, const std::optional<std::vector<std::optional<LutProperties>>>& properties); }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index f934cb20a0..e42b9b12f4 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -253,7 +253,6 @@ private: std::unordered_map<size_t, size_t> mFinalLayerCounts; size_t mCachedSetCreationCount = 0; size_t mCachedSetCreationCost = 0; - std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges; }; } // namespace compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index d2a5a2066c..f65a9083c5 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -52,6 +52,7 @@ public: MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>()); MOCK_METHOD1(setReleaseFence, void(const FenceResult&)); + MOCK_METHOD1(setReleasedBuffer, void(sp<GraphicBuffer>)); MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus()); MOCK_CONST_METHOD0(getDebugName, const char*()); MOCK_CONST_METHOD0(getSequence, int32_t()); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index f2c265ad2e..eaa3dd37ec 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -35,6 +35,7 @@ public: MOCK_CONST_METHOD0(isValid, bool()); MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>()); + MOCK_CONST_METHOD0(getDisplayIdVariant, ftl::Optional<DisplayIdVariant>()); MOCK_METHOD1(setCompositionEnabled, void(bool)); MOCK_METHOD1(setLayerCachingEnabled, void(bool)); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 09c47f0224..be36db6fd1 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -60,7 +60,7 @@ public: MOCK_CONST_METHOD0(needsFiltering, bool()); MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>()); MOCK_METHOD(void, applyDeviceLayerLut, - (ndk::ScopedFileDescriptor, + (::android::base::unique_fd, (std::vector<std::pair< int, aidl::android::hardware::graphics::composer3::LutProperties>>))); MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const)); diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp index d9018bc3ab..dc841952c9 100644 --- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp @@ -38,7 +38,8 @@ inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs, lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow && lhs.backgroundBlurRadius == rhs.backgroundBlurRadius && lhs.stretchEffect == rhs.stretchEffect && - lhs.edgeExtensionEffect == rhs.edgeExtensionEffect; + lhs.edgeExtensionEffect == rhs.edgeExtensionEffect && + lhs.whitePointNits == rhs.whitePointNits; } inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) { diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index 989f8e3c5e..ab2a03cd60 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -91,13 +91,13 @@ nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const { namespace { void offloadOutputs(Outputs& outputs) { - if (!FlagManager::getInstance().multithreaded_present() || outputs.size() < 2) { + if (outputs.size() < 2) { return; } ui::PhysicalDisplayVector<compositionengine::Output*> outputsToOffload; for (const auto& output : outputs) { - if (!ftl::Optional(output->getDisplayId()).and_then(HalDisplayId::tryCast)) { + if (!output->getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) { // Not HWC-enabled, so it is always client-composited. No need to offload. continue; } diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index e37ce0a0eb..5a546777f4 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -52,7 +52,7 @@ std::shared_ptr<Display> createDisplay( Display::~Display() = default; void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) { - mId = args.id; + mIdVariant = args.idVariant; mPowerAdvisor = args.powerAdvisor; mHasPictureProcessing = args.hasPictureProcessing; mMaxLayerPictureProfiles = args.maxLayerPictureProfiles; @@ -67,7 +67,7 @@ bool Display::isValid() const { } DisplayId Display::getId() const { - return mId; + return asDisplayId(mIdVariant); } bool Display::isSecure() const { @@ -79,11 +79,15 @@ void Display::setSecure(bool secure) { } bool Display::isVirtual() const { - return mId.isVirtual(); + return !std::holds_alternative<PhysicalDisplayId>(mIdVariant); } ftl::Optional<DisplayId> Display::getDisplayId() const { - return mId; + return getId(); +} + +ftl::Optional<DisplayIdVariant> Display::getDisplayIdVariant() const { + return mIdVariant; } void Display::disconnect() { @@ -93,14 +97,14 @@ void Display::disconnect() { mIsDisconnected = true; - if (const auto id = HalDisplayId::tryCast(mId)) { + if (const auto id = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) { getCompositionEngine().getHwComposer().disconnectDisplay(*id); } } void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) { Output::setColorTransform(args); - const auto halDisplayId = HalDisplayId::tryCast(mId); + const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>); if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) { return; } @@ -108,7 +112,7 @@ void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& auto& hwc = getCompositionEngine().getHwComposer(); status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix); ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d", - to_string(mId).c_str(), result); + to_string(*halDisplayId).c_str(), result); } void Display::setColorProfile(const ColorProfile& colorProfile) { @@ -125,7 +129,7 @@ void Display::setColorProfile(const ColorProfile& colorProfile) { Output::setColorProfile(colorProfile); - const auto physicalId = PhysicalDisplayId::tryCast(mId); + const auto physicalId = getDisplayIdVariant().and_then(asPhysicalDisplayId); LOG_FATAL_IF(!physicalId); getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode, colorProfile.renderIntent); @@ -133,7 +137,7 @@ void Display::setColorProfile(const ColorProfile& colorProfile) { void Display::dump(std::string& out) const { const char* const type = isVirtual() ? "virtual" : "physical"; - base::StringAppendF(&out, "Display %s (%s, \"%s\")", to_string(mId).c_str(), type, + base::StringAppendF(&out, "Display %s (%s, \"%s\")", to_string(getId()).c_str(), type, getName().c_str()); out.append("\n Composition Display State:\n"); @@ -157,7 +161,7 @@ std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer( const sp<compositionengine::LayerFE>& layerFE) const { auto outputLayer = impl::createOutputLayer(*this, layerFE); - if (const auto halDisplayId = HalDisplayId::tryCast(mId); + if (const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>); outputLayer && !mIsDisconnected && halDisplayId) { auto& hwc = getCompositionEngine().getHwComposer(); auto hwcLayer = hwc.createLayer(*halDisplayId); @@ -171,8 +175,7 @@ std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer( void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) { Output::setReleasedLayers(refreshArgs); - if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) || - refreshArgs.layersWithQueuedFrames.empty()) { + if (mIsDisconnected || isGpuVirtualDisplay() || refreshArgs.layersWithQueuedFrames.empty()) { return; } @@ -208,7 +211,7 @@ void Display::applyDisplayBrightness(bool applyImmediately) { if (!getState().displayBrightness) { return; } - if (auto displayId = PhysicalDisplayId::tryCast(mId)) { + if (auto displayId = getDisplayIdVariant().and_then(asPhysicalDisplayId)) { auto& hwc = getCompositionEngine().getHwComposer(); status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness, getState().displayBrightnessNits, @@ -226,7 +229,7 @@ void Display::beginFrame() { Output::beginFrame(); // If we don't have a HWC display, then we are done. - const auto halDisplayId = HalDisplayId::tryCast(mId); + const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>); if (!halDisplayId) { return; } @@ -244,7 +247,7 @@ bool Display::chooseCompositionStrategy( } // If we don't have a HWC display, then we are done. - const auto halDisplayId = HalDisplayId::tryCast(mId); + const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>); if (!halDisplayId) { return false; } @@ -266,9 +269,9 @@ bool Display::chooseCompositionStrategy( } if (isPowerHintSessionEnabled()) { - mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now()); - if (auto halDisplayId = HalDisplayId::tryCast(mId)) { - mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId)); + mPowerAdvisor->setHwcValidateTiming(getId(), hwcValidateStartTime, TimePoint::now()); + if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) { + mPowerAdvisor->setSkippedValidate(*halDisplayId, hwc.getValidateSkipped(*halDisplayId)); } } @@ -292,7 +295,7 @@ void Display::applyCompositionStrategy(const std::optional<DeviceRequestedChange bool Display::getSkipColorTransform() const { auto& hwc = getCompositionEngine().getHwComposer(); - if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) { return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); } @@ -373,7 +376,7 @@ void Display::applyLayerLutsToLayers(const LayerLuts& layerLuts) { if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) { if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) { - layer->applyDeviceLayerLut(ndk::ScopedFileDescriptor(mapperIt->second.release()), + layer->applyDeviceLayerLut(::android::base::unique_fd(mapperIt->second.release()), lutsIt->second); } } @@ -383,7 +386,7 @@ void Display::applyLayerLutsToLayers(const LayerLuts& layerLuts) { } void Display::executeCommands() { - const auto halDisplayIdOpt = HalDisplayId::tryCast(mId); + const auto halDisplayIdOpt = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>); if (mIsDisconnected || !halDisplayIdOpt) { return; } @@ -394,7 +397,7 @@ void Display::executeCommands() { compositionengine::Output::FrameFences Display::presentFrame() { auto fences = impl::Output::presentFrame(); - const auto halDisplayIdOpt = HalDisplayId::tryCast(mId); + const auto halDisplayIdOpt = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>); if (mIsDisconnected || !halDisplayIdOpt) { return fences; } @@ -404,13 +407,13 @@ compositionengine::Output::FrameFences Display::presentFrame() { const TimePoint startTime = TimePoint::now(); if (isPowerHintSessionEnabled() && getState().earliestPresentTime) { - mPowerAdvisor->setHwcPresentDelayedTime(mId, *getState().earliestPresentTime); + mPowerAdvisor->setHwcPresentDelayedTime(*halDisplayIdOpt, *getState().earliestPresentTime); } hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime); if (isPowerHintSessionEnabled()) { - mPowerAdvisor->setHwcPresentTiming(mId, startTime, TimePoint::now()); + mPowerAdvisor->setHwcPresentTiming(*halDisplayIdOpt, startTime, TimePoint::now()); } fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt); @@ -433,8 +436,8 @@ compositionengine::Output::FrameFences Display::presentFrame() { void Display::setExpensiveRenderingExpected(bool enabled) { Output::setExpensiveRenderingExpected(enabled); - if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) { - mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled); + if (mPowerAdvisor && !isGpuVirtualDisplay()) { + mPowerAdvisor->setExpensiveRenderingExpected(getId(), enabled); } } @@ -449,15 +452,15 @@ bool Display::isPowerHintSessionGpuReportingEnabled() { // For ADPF GPU v0 this is expected to set start time to when the GPU commands are submitted with // fence returned, i.e. when RenderEngine flushes the commands and returns the draw fence. void Display::setHintSessionGpuStart(TimePoint startTime) { - mPowerAdvisor->setGpuStartTime(mId, startTime); + mPowerAdvisor->setGpuStartTime(getId(), startTime); } void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) { - mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence)); + mPowerAdvisor->setGpuFenceTime(getId(), std::move(gpuFence)); } void Display::setHintSessionRequiresRenderEngine(bool requiresRenderEngine) { - mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine); + mPowerAdvisor->setRequiresRenderEngine(getId(), requiresRenderEngine); } const aidl::android::hardware::graphics::composer3::OverlayProperties* @@ -478,7 +481,7 @@ void Display::finishFrame(GpuCompositionResult&& result) { // 1) It is being handled by hardware composer, which may need this to // keep its virtual display state machine in sync, or // 2) There is work to be done (the dirty region isn't empty) - if (GpuVirtualDisplayId::tryCast(mId) && !mustRecompose()) { + if (isGpuVirtualDisplay() && !mustRecompose()) { ALOGV("Skipping display composition"); return; } @@ -487,7 +490,7 @@ void Display::finishFrame(GpuCompositionResult&& result) { } bool Display::supportsOffloadPresent() const { - if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) { auto& hwc = getCompositionEngine().getHwComposer(); return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT); } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index de1d13a8d1..00a61a5ab6 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -118,6 +118,10 @@ ftl::Optional<DisplayId> Output::getDisplayId() const { return {}; } +ftl::Optional<DisplayIdVariant> Output::getDisplayIdVariant() const { + return {}; +} + const std::string& Output::getName() const { return mName; } @@ -436,8 +440,8 @@ void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArg ftl::Future<std::monostate> Output::present( const compositionengine::CompositionRefreshArgs& refreshArgs) { const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string { - return getDisplayId() - .and_then(PhysicalDisplayId::tryCast) + return getDisplayIdVariant() + .and_then(asPhysicalDisplayId) .and_then([&refreshArgs](PhysicalDisplayId id) { return refreshArgs.frameTargets.get(id); }) @@ -890,8 +894,8 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr return; } - if (auto frameTargetPtrOpt = getDisplayId() - .and_then(PhysicalDisplayId::tryCast) + if (auto frameTargetPtrOpt = getDisplayIdVariant() + .and_then(asPhysicalDisplayId) .and_then([&refreshArgs](PhysicalDisplayId id) { return refreshArgs.frameTargets.get(id); })) { @@ -1389,7 +1393,8 @@ std::optional<base::unique_fd> Output::composeSurfaces( // or complex GPU shaders and it's expensive. We boost the GPU frequency so that // GPU composition can finish in time. We must reset GPU frequency afterwards, // because high frequency consumes extra battery. - const bool expensiveRenderingExpected = + const bool expensiveBlurs = mLayerRequestingBackgroundBlur != nullptr; + const bool expensiveRenderingExpected = expensiveBlurs || std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(), [outputDataspace = clientCompositionDisplay.outputDataspace](const auto& layer) { @@ -1671,6 +1676,7 @@ void Output::presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) { Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence); } layer->getLayerFE().setReleaseFence(releaseFence); + layer->getLayerFE().setReleasedBuffer(layer->getLayerFE().getCompositionState()->buffer); } // We've got a list of layers needing fences, that are disjoint with @@ -1840,7 +1846,7 @@ void Output::applyPictureProfile() { if (!getDisplayId()) { return; } - if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) { + if (auto displayId = getDisplayIdVariant().and_then(asPhysicalDisplayId)) { auto& hwc = getCompositionEngine().getHwComposer(); const status_t error = hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index d89b52d632..ea360115c1 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -369,8 +369,11 @@ void OutputLayer::updateCompositionState( layerFEState->buffer->getPixelFormat())) : std::nullopt; - auto hdrRenderType = - getHdrRenderType(outputState.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio); + // prefer querying this from gralloc instead to catch 2094-10 metadata + const bool hasHdrMetadata = layerFEState->hdrMetadata.validTypes != 0; + + auto hdrRenderType = getHdrRenderType(outputState.dataspace, pixelFormat, + layerFEState->desiredHdrSdrRatio, hasHdrMetadata); // Determine the output dependent dataspace for this layer. If it is // colorspace agnostic, it just uses the dataspace chosen for the output to @@ -393,8 +396,8 @@ void OutputLayer::updateCompositionState( } // re-get HdrRenderType after the dataspace gets changed. - hdrRenderType = - getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio); + hdrRenderType = getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio, + hasHdrMetadata); // For hdr content, treat the white point as the display brightness - HDR content should not be // boosted or dimmed. @@ -416,12 +419,20 @@ void OutputLayer::updateCompositionState( state.dimmingRatio = std::min(idealizedMaxHeadroom / deviceHeadroom, 1.0f); state.whitePointNits = getOutput().getState().displayBrightnessNits * state.dimmingRatio; } else { + const bool isLayerFp16 = pixelFormat && *pixelFormat == ui::PixelFormat::RGBA_FP16; float layerBrightnessNits = getOutput().getState().sdrWhitePointNits; // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular // range that we may need to re-adjust to the current display conditions + // Do NOT do this when we may render fp16 to an fp16 client target, to avoid applying + // and additional gain to the layer. This is because the fp16 client target should + // already be adapted to remap 1.0 to the SDR white point in the panel's luminance + // space. if (hdrRenderType == HdrRenderType::DISPLAY_HDR) { - layerBrightnessNits *= layerFEState->currentHdrSdrRatio; + if (!FlagManager::getInstance().fp16_client_target() || !isLayerFp16) { + layerBrightnessNits *= layerFEState->currentHdrSdrRatio; + } } + state.dimmingRatio = std::clamp(layerBrightnessNits / getOutput().getState().displayBrightnessNits, 0.f, 1.f); @@ -619,7 +630,7 @@ void OutputLayer::writeLutToHWC(HWC2::Layer* hwcLayer, lutProperties[i].samplingKey)}}); } - luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get())); + luts.pfd.set(dup(lutFileDescriptor.get())); luts.offsets = lutOffsets; luts.lutProperties = std::move(aidlProperties); } @@ -865,8 +876,7 @@ void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer, bool isPeekingThrough, bool skipLayer) { auto& outputDependentState = editState(); - bool isCached = !skipLayer && outputDependentState.overrideInfo.buffer; - if (isClientCompositionForced(isPeekingThrough, isCached)) { + if (isClientCompositionForced(isPeekingThrough)) { // If we are forcing client composition, we need to tell the HWC requestedCompositionType = Composition::CLIENT; } @@ -956,12 +966,9 @@ void OutputLayer::detectDisallowedCompositionTypeChange(Composition from, Compos } } -bool OutputLayer::isClientCompositionForced(bool isPeekingThrough, bool isCached) const { - // If this layer was flattened into a CachedSet then it is not necessary for - // the GPU to compose it. - bool requiresClientDrawnRoundedCorners = !isCached && getLayerFE().hasRoundedCorners(); +bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const { return getState().forceClientComposition || - (!isPeekingThrough && requiresClientDrawnRoundedCorners); + (!isPeekingThrough && getLayerFE().hasRoundedCorners()); } void OutputLayer::applyDeviceCompositionTypeChange(Composition compositionType) { @@ -1006,7 +1013,7 @@ void OutputLayer::applyDeviceLayerRequest(hal::LayerRequest request) { } void OutputLayer::applyDeviceLayerLut( - ndk::ScopedFileDescriptor lutFileDescriptor, + ::android::base::unique_fd lutFd, std::vector<std::pair<int, LutProperties>> lutOffsetsAndProperties) { auto& state = editState(); LOG_FATAL_IF(!state.hwc); @@ -1025,9 +1032,9 @@ void OutputLayer::applyDeviceLayerLut( samplingKeys.emplace_back(static_cast<int32_t>(properties.samplingKeys[0])); } } - hwcState.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(lutFileDescriptor.release()), - std::move(offsets), std::move(dimensions), - std::move(sizes), std::move(samplingKeys)); + hwcState.luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), std::move(offsets), + std::move(dimensions), std::move(sizes), + std::move(samplingKeys)); } bool OutputLayer::needsFiltering() const { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 783209c26a..2081cd5400 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -243,17 +243,9 @@ void Flattener::resetActivities(NonBufferHash hash, time_point now) { mCurrentGeometry = hash; mLastGeometryUpdate = now; - - for (const CachedSet& cachedSet : mLayers) { - if (cachedSet.getLayerCount() > 1) { - ++mInvalidatedCachedSetAges[cachedSet.getAge()]; - } - } - mLayers.clear(); if (mNewCachedSet) { - ++mInvalidatedCachedSetAges[mNewCachedSet->getAge()]; mNewCachedSet = std::nullopt; } } @@ -312,7 +304,6 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) { if (mNewCachedSet->hasBufferUpdate()) { ALOGV("[%s] Dropping new cached set", __func__); - ++mInvalidatedCachedSetAges[0]; mNewCachedSet = std::nullopt; } else if (mNewCachedSet->hasReadyBuffer()) { ALOGV("[%s] Found ready buffer", __func__); @@ -325,6 +316,7 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers priorBlurLayer == (*incomingLayerIter)->getOutputLayer(); OutputLayer::CompositionState& state = (*incomingLayerIter)->getOutputLayer()->editState(); + state.overrideInfo = { .buffer = mNewCachedSet->getBuffer(), .acquireFence = mNewCachedSet->getDrawFence(), @@ -338,10 +330,6 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers }; ++incomingLayerIter; } - - if (currentLayerIter->getLayerCount() > 1) { - ++mInvalidatedCachedSetAges[currentLayerIter->getAge()]; - } ++currentLayerIter; skipCount -= layerCount; @@ -378,9 +366,9 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers }; ++incomingLayerIter; } + priorBlurLayer = currentLayerIter->getBlurLayer(); } else if (currentLayerIter->getLayerCount() > 1) { // Break the current layer into its constituent layers - ++mInvalidatedCachedSetAges[currentLayerIter->getAge()]; for (CachedSet& layer : currentLayerIter->decompose()) { bool disableBlur = priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer(); @@ -400,8 +388,8 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers currentLayerIter->updateAge(now); merged.emplace_back(*currentLayerIter); ++incomingLayerIter; + priorBlurLayer = currentLayerIter->getBlurLayer(); } - priorBlurLayer = currentLayerIter->getBlurLayer(); ++currentLayerIter; } diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index ad65c4422e..34c09db6f8 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -43,6 +43,10 @@ using ::testing::ReturnRef; using ::testing::SaveArg; using ::testing::StrictMock; +static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u); +static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u); +static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(567u); + struct CompositionEngineTest : public testing::Test { std::shared_ptr<TimeStats> mTimeStats; @@ -52,6 +56,31 @@ struct CompositionEngineTest : public testing::Test { std::shared_ptr<mock::Output> mOutput1{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mOutput2{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mOutput3{std::make_shared<StrictMock<mock::Output>>()}; + + std::array<impl::OutputCompositionState, 3> mOutputStates; + + void SetUp() override { + EXPECT_CALL(*mOutput1, getDisplayId) + .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1))); + EXPECT_CALL(*mOutput1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1)); + + EXPECT_CALL(*mOutput2, getDisplayId) + .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2))); + EXPECT_CALL(*mOutput2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2)); + + EXPECT_CALL(*mOutput3, getDisplayId) + .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId3))); + EXPECT_CALL(*mOutput3, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId3)); + + // Most tests will depend on the outputs being enabled. + for (auto& state : mOutputStates) { + state.isEnabled = true; + } + + EXPECT_CALL(*mOutput1, getState).WillRepeatedly(ReturnRef(mOutputStates[0])); + EXPECT_CALL(*mOutput2, getState).WillRepeatedly(ReturnRef(mOutputStates[1])); + EXPECT_CALL(*mOutput3, getState).WillRepeatedly(ReturnRef(mOutputStates[2])); + } }; TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) { @@ -94,7 +123,7 @@ struct CompositionEnginePresentTest : public CompositionEngineTest { StrictMock<CompositionEnginePartialMock> mEngine; }; -TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) { +TEST_F(CompositionEnginePresentTest, zeroOutputs) { // present() always calls preComposition() and postComposition() EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs))); EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs))); @@ -102,7 +131,7 @@ TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) { mEngine.present(mRefreshArgs); } -TEST_F(CompositionEnginePresentTest, worksAsExpected) { +TEST_F(CompositionEnginePresentTest, threeOutputs) { // Expect calls to in a certain sequence InSequence seq; @@ -114,9 +143,7 @@ TEST_F(CompositionEnginePresentTest, worksAsExpected) { EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _)); EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _)); - // All of mOutput<i> are StrictMocks. If the flag is true, it will introduce - // calls to getDisplayId, which are not relevant to this test. - SET_FLAG_FOR_TEST(flags::multithreaded_present, false); + EXPECT_CALL(*mOutput1, supportsOffloadPresent).WillOnce(Return(false)); // The last step is to actually present each output. EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs))) @@ -284,8 +311,6 @@ struct CompositionEngineOffloadTest : public testing::Test { std::shared_ptr<mock::Output> mVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()}; std::shared_ptr<mock::Output> mHalVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()}; - static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u); - static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u); static constexpr GpuVirtualDisplayId kGpuVirtualDisplayId{789u}; static constexpr HalVirtualDisplayId kHalVirtualDisplayId{456u}; @@ -294,12 +319,23 @@ struct CompositionEngineOffloadTest : public testing::Test { void SetUp() override { EXPECT_CALL(*mDisplay1, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1))); + EXPECT_CALL(*mDisplay1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1)); + EXPECT_CALL(*mDisplay2, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2))); + EXPECT_CALL(*mDisplay2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2)); + EXPECT_CALL(*mVirtualDisplay, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kGpuVirtualDisplayId))); + const DisplayIdVariant gpuVariant = + GpuVirtualDisplayId::fromValue(kGpuVirtualDisplayId.value); + EXPECT_CALL(*mVirtualDisplay, getDisplayIdVariant).WillRepeatedly(Return(gpuVariant)); + EXPECT_CALL(*mHalVirtualDisplay, getDisplayId) .WillRepeatedly(Return(std::make_optional<DisplayId>(kHalVirtualDisplayId))); + const DisplayIdVariant halVariant = + HalVirtualDisplayId::fromValue(kHalVirtualDisplayId.value); + EXPECT_CALL(*mHalVirtualDisplay, getDisplayIdVariant).WillRepeatedly(Return(halVariant)); // Most tests will depend on the outputs being enabled. for (auto& state : mOutputStates) { @@ -332,7 +368,6 @@ TEST_F(CompositionEngineOffloadTest, basic) { EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); @@ -345,7 +380,6 @@ TEST_F(CompositionEngineOffloadTest, dependsOnSupport) { EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); @@ -358,20 +392,6 @@ TEST_F(CompositionEngineOffloadTest, dependsOnSupport2) { EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); - setOutputs({mDisplay1, mDisplay2}); - - mEngine.present(mRefreshArgs); -} - -TEST_F(CompositionEngineOffloadTest, dependsOnFlag) { - EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0); - EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0); - - EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); - EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); - - SET_FLAG_FOR_TEST(flags::multithreaded_present, false); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); @@ -382,7 +402,6 @@ TEST_F(CompositionEngineOffloadTest, oneDisplay) { EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1}); mEngine.present(mRefreshArgs); @@ -397,7 +416,6 @@ TEST_F(CompositionEngineOffloadTest, virtualDisplay) { EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2, mVirtualDisplay}); mEngine.present(mRefreshArgs); @@ -410,7 +428,6 @@ TEST_F(CompositionEngineOffloadTest, virtualDisplay2) { EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mVirtualDisplay}); mEngine.present(mRefreshArgs); @@ -423,7 +440,6 @@ TEST_F(CompositionEngineOffloadTest, halVirtual) { EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mHalVirtualDisplay}); mEngine.present(mRefreshArgs); @@ -440,7 +456,6 @@ TEST_F(CompositionEngineOffloadTest, ordering) { EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mVirtualDisplay, mHalVirtualDisplay, mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); @@ -458,7 +473,6 @@ TEST_F(CompositionEngineOffloadTest, dependsOnEnabled) { EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2}); mEngine.present(mRefreshArgs); @@ -478,7 +492,6 @@ TEST_F(CompositionEngineOffloadTest, disabledDisplaysDoNotPreventOthersFromOfflo EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); setOutputs({mDisplay1, mDisplay2, mHalVirtualDisplay}); mEngine.present(mRefreshArgs); diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index c1e59d01de..77fd4466ef 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -278,7 +278,7 @@ TEST_F(DisplayCreationTest, createGpuVirtualDisplay) { impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForGpuVirtualDisplay()); EXPECT_FALSE(display->isSecure()); EXPECT_TRUE(display->isVirtual()); - EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId())); + EXPECT_TRUE(display->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>)); } /* @@ -318,6 +318,7 @@ TEST_F(DisplaySetConfigurationTest, configuresHalVirtualDisplay) { EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId()); EXPECT_FALSE(mDisplay->isSecure()); EXPECT_TRUE(mDisplay->isVirtual()); + EXPECT_TRUE(mDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<HalVirtualDisplayId>)); EXPECT_FALSE(mDisplay->isValid()); const auto& filter = mDisplay->getState().layerFilter; @@ -337,6 +338,7 @@ TEST_F(DisplaySetConfigurationTest, configuresGpuVirtualDisplay) { EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId()); EXPECT_FALSE(mDisplay->isSecure()); EXPECT_TRUE(mDisplay->isVirtual()); + EXPECT_TRUE(mDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>)); EXPECT_FALSE(mDisplay->isValid()); const auto& filter = mDisplay->getState().layerFilter; @@ -572,7 +574,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfGpuDisplay) { auto args = getDisplayCreationArgsForGpuVirtualDisplay(); std::shared_ptr<Display> gpuDisplay = createPartialMockDisplay<Display>(mCompositionEngine, args); - EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId())); + EXPECT_TRUE(gpuDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>)); chooseCompositionStrategy(gpuDisplay.get()); diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 09ad9fa497..590626ace5 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -148,20 +148,23 @@ struct OutputTest : public testing::Test { virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0; virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; } + virtual ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override { + return DisplayIdVariant(mId); + } virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; } virtual int32_t getMaxLayerPictureProfiles() const override { return mMaxLayerPictureProfiles; } - void setDisplayIdForTest(DisplayId value) { mId = value; } + void setDisplayIdForTest(PhysicalDisplayId value) { mId = value; } void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; } void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; } private: - ftl::Optional<DisplayId> mId; + PhysicalDisplayId mId; bool mHasPictureProcessing; int32_t mMaxLayerPictureProfiles; }; @@ -3311,6 +3314,17 @@ TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) { sp<Fence> layer2Fence = sp<Fence>::make(); sp<Fence> layer3Fence = sp<Fence>::make(); + // Set up layerfe buffers + LayerFECompositionState layer1State; + layer1State.buffer = sp<GraphicBuffer>::make(); + LayerFECompositionState layer2State; + layer2State.buffer = sp<GraphicBuffer>::make(); + LayerFECompositionState layer3State; + layer3State.buffer = nullptr; + EXPECT_CALL(*mLayer1.layerFE, getCompositionState()).WillOnce(Return(&layer1State)); + EXPECT_CALL(*mLayer2.layerFE, getCompositionState()).WillOnce(Return(&layer2State)); + EXPECT_CALL(*mLayer3.layerFE, getCompositionState()).WillOnce(Return(&layer3State)); + Output::FrameFences frameFences; frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence); frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence); @@ -3327,14 +3341,23 @@ TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) { .WillOnce([&layer1Fence](FenceResult releaseFence) { EXPECT_EQ(FenceResult(layer1Fence), releaseFence); }); + EXPECT_CALL(*mLayer1.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) { + EXPECT_EQ(layer1State.buffer, buffer); + }); EXPECT_CALL(*mLayer2.layerFE, setReleaseFence(_)) .WillOnce([&layer2Fence](FenceResult releaseFence) { EXPECT_EQ(FenceResult(layer2Fence), releaseFence); }); + EXPECT_CALL(*mLayer2.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) { + EXPECT_EQ(layer2State.buffer, buffer); + }); EXPECT_CALL(*mLayer3.layerFE, setReleaseFence(_)) .WillOnce([&layer3Fence](FenceResult releaseFence) { EXPECT_EQ(FenceResult(layer3Fence), releaseFence); }); + EXPECT_CALL(*mLayer3.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) { + EXPECT_EQ(layer3State.buffer, buffer); + }); constexpr bool kFlushEvenWhenDisabled = false; mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled); @@ -3350,6 +3373,17 @@ TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFenc frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make()); frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make()); + // Set up layerfe buffers + LayerFECompositionState layer1State; + layer1State.buffer = sp<GraphicBuffer>::make(); + LayerFECompositionState layer2State; + layer2State.buffer = sp<GraphicBuffer>::make(); + LayerFECompositionState layer3State; + layer3State.buffer = nullptr; + EXPECT_CALL(*mLayer1.layerFE, getCompositionState()).WillOnce(Return(&layer1State)); + EXPECT_CALL(*mLayer2.layerFE, getCompositionState()).WillOnce(Return(&layer2State)); + EXPECT_CALL(*mLayer3.layerFE, getCompositionState()).WillOnce(Return(&layer3State)); + EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); @@ -3359,6 +3393,15 @@ TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFenc EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return()); EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return()); EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return()); + EXPECT_CALL(*mLayer1.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) { + EXPECT_EQ(layer1State.buffer, buffer); + }); + EXPECT_CALL(*mLayer2.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) { + EXPECT_EQ(layer2State.buffer, buffer); + }); + EXPECT_CALL(*mLayer3.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) { + EXPECT_EQ(layer3State.buffer, buffer); + }); constexpr bool kFlushEvenWhenDisabled = false; mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled); } @@ -3401,7 +3444,6 @@ TEST_F(OutputPostFramebufferTest, setReleasedLayersSentPresentFence) { .WillOnce([&presentFence](FenceResult fenceResult) { EXPECT_EQ(FenceResult(presentFence), fenceResult); }); - constexpr bool kFlushEvenWhenDisabled = false; mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled); diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp index a086aee847..87a677cd58 100644 --- a/services/surfaceflinger/Display/DisplayModeController.cpp +++ b/services/surfaceflinger/Display/DisplayModeController.cpp @@ -97,9 +97,7 @@ auto DisplayModeController::setDesiredMode(PhysicalDisplayId displayId, const bool force = desiredModeOpt->force; desiredModeOpt = std::move(desiredMode); desiredModeOpt->emitEvent |= emitEvent; - if (FlagManager::getInstance().connected_display()) { - desiredModeOpt->force |= force; - } + desiredModeOpt->force |= force; return DesiredModeAction::None; } @@ -191,7 +189,7 @@ auto DisplayModeController::initiateModeChange( // 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(displayPtr->desiredModeLock); if (displayPtr->desiredModeOpt) { displayPtr->desiredModeOpt->force = false; diff --git a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h index c68020ce51..71d9f2e468 100644 --- a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h +++ b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h @@ -35,6 +35,7 @@ public: VirtualDisplayId displayId() const { return mVirtualId; } bool isGpu() const { return mIsGpu; } + const std::string& uniqueId() const { return mUniqueId; } void dump(utils::Dumper& dumper) const { using namespace std::string_view_literals; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index e8b09b043e..de7d455fa4 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -26,7 +26,6 @@ #include <common/trace.h> #include <compositionengine/CompositionEngine.h> -#include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> #include <compositionengine/DisplayColorProfileCreationArgs.h> #include <compositionengine/DisplayCreationArgs.h> @@ -169,8 +168,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 (!isVirtual() && (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON)) { if (mStagedBrightness && mBrightness != mStagedBrightness) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); mBrightness = *mStagedBrightness; @@ -283,6 +281,7 @@ void DisplayDevice::dump(utils::Dumper& dumper) const { dumper.dump("name"sv, '"' + mDisplayName + '"'); dumper.dump("powerMode"sv, mPowerMode); + dumper.dump("optimizationPolicy"sv, mOptimizationPolicy); if (mRefreshRateSelector) { mRefreshRateSelector->dump(dumper); @@ -297,6 +296,10 @@ DisplayId DisplayDevice::getId() const { return mCompositionDisplay->getId(); } +bool DisplayDevice::isVirtual() const { + return mCompositionDisplay->isVirtual(); +} + bool DisplayDevice::isSecure() const { return mCompositionDisplay->isSecure(); } @@ -305,6 +308,15 @@ void DisplayDevice::setSecure(bool secure) { mCompositionDisplay->setSecure(secure); } +gui::ISurfaceComposer::OptimizationPolicy DisplayDevice::getOptimizationPolicy() const { + return mOptimizationPolicy; +} + +void DisplayDevice::setOptimizationPolicy( + gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) { + mOptimizationPolicy = optimizationPolicy; +} + const Rect DisplayDevice::getBounds() const { return mCompositionDisplay->getState().displaySpace.getBoundsAsRect(); } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index b5a543a7c1..1b14145147 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -23,6 +23,8 @@ #include <android-base/thread_annotations.h> #include <android/native_window.h> #include <binder/IBinder.h> +#include <compositionengine/Display.h> +#include <compositionengine/DisplaySurface.h> #include <gui/LayerState.h> #include <math/mat4.h> #include <renderengine/RenderEngine.h> @@ -61,15 +63,21 @@ class SurfaceFlinger; struct CompositionInfo; struct DisplayDeviceCreationArgs; -namespace compositionengine { -class Display; -class DisplaySurface; -} // namespace compositionengine - namespace display { class DisplaySnapshot; } // namespace display +namespace gui { +inline const char* to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) { + switch (optimizationPolicy) { + case ISurfaceComposer::OptimizationPolicy::optimizeForPower: + return "optimizeForPower"; + case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance: + return "optimizeForPerformance"; + } +} +} // namespace gui + class DisplayDevice : public RefBase { public: constexpr static float sDefaultMinLumiance = 0.0; @@ -85,7 +93,7 @@ public: return mCompositionDisplay; } - bool isVirtual() const { return getId().isVirtual(); } + bool isVirtual() const; bool isPrimary() const { return mIsPrimary; } // isSecure indicates whether this display can be trusted to display @@ -93,6 +101,11 @@ public: bool isSecure() const; void setSecure(bool secure); + // The optimization policy influences whether this display is optimized for power or + // performance. + gui::ISurfaceComposer::OptimizationPolicy getOptimizationPolicy() const; + void setOptimizationPolicy(gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy); + int getWidth() const; int getHeight() const; ui::Size getSize() const { return {getWidth(), getHeight()}; } @@ -118,17 +131,30 @@ public: DisplayId getId() const; + DisplayIdVariant getDisplayIdVariant() const { + const auto idVariant = mCompositionDisplay->getDisplayIdVariant(); + LOG_FATAL_IF(!idVariant); + return *idVariant; + } + + std::optional<VirtualDisplayIdVariant> getVirtualDisplayIdVariant() const { + return ftl::match( + getDisplayIdVariant(), + [](PhysicalDisplayId) { return std::optional<VirtualDisplayIdVariant>(); }, + [](auto id) { return std::optional<VirtualDisplayIdVariant>(id); }); + } + // Shorthand to upcast the ID of a display whose type is known as a precondition. PhysicalDisplayId getPhysicalId() const { - const auto id = PhysicalDisplayId::tryCast(getId()); - LOG_FATAL_IF(!id); - return *id; + const auto physicalDisplayId = asPhysicalDisplayId(getDisplayIdVariant()); + LOG_FATAL_IF(!physicalDisplayId); + return *physicalDisplayId; } VirtualDisplayId getVirtualId() const { - const auto id = VirtualDisplayId::tryCast(getId()); - LOG_FATAL_IF(!id); - return *id; + const auto virtualDisplayId = asVirtualDisplayId(getDisplayIdVariant()); + LOG_FATAL_IF(!virtualDisplayId); + return *virtualDisplayId; } const wp<IBinder>& getDisplayToken() const { return mDisplayToken; } @@ -236,6 +262,9 @@ private: // TODO(b/182939859): Remove special cases for primary display. const bool mIsPrimary; + gui::ISurfaceComposer::OptimizationPolicy mOptimizationPolicy = + gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance; + uint32_t mFlags = 0; // Requested refresh rate in fps, supported only for virtual displays. @@ -283,11 +312,16 @@ struct DisplayDeviceState { std::string displayName; std::string uniqueId; bool isSecure = false; + + gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy = + gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance; bool isProtected = false; // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only Fps requestedRefreshRate; int32_t maxLayerPictureProfiles = 0; bool hasPictureProcessing = false; + hardware::graphics::composer::hal::PowerMode initialPowerMode{ + hardware::graphics::composer::hal::PowerMode::OFF}; private: static std::atomic<int32_t> sNextSequenceId; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index bb6bebed8d..d547af98ea 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -1773,7 +1773,6 @@ void AidlComposer::onHotplugDisconnect(Display display) { } bool AidlComposer::hasMultiThreadedPresentSupport(Display display) { - if (!FlagManager::getInstance().multithreaded_present()) return false; const auto displayId = translate<int64_t>(display); std::vector<AidlDisplayCapability> capabilities; const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities); diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 252c6b692d..8d16a6bbb3 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -439,11 +439,8 @@ Error Display::setActiveConfigWithConstraints(hal::HWConfigId configId, // 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)) { + getConnectionType().value_opt() != ui::DisplayConnectionType::External) { Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints; hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos; hwc2Constraints.seamlessRequired = constraints.seamlessRequired; @@ -638,7 +635,7 @@ Error Display::getRequestedLuts(LayerLuts* outLuts, [](int32_t i, LutProperties j) { return std::make_pair(i, j); }); outLuts->emplace_or_replace(layer.get(), lutOffsetsAndProperties); lutFileDescriptorMapper.emplace_or_replace(layer.get(), - ndk::ScopedFileDescriptor( + ::android::base::unique_fd( layerLut.luts.pfd.release())); } else { ALOGE("getRequestedLuts: invalid luts on layer %" PRIu64 " found" diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index c3deb8429a..7c1f8e3da0 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -115,7 +115,7 @@ public: using LayerLuts = ftl::SmallMap<HWC2::Layer*, LutOffsetAndProperties, kLutFileDescriptorMapperSize>; using LutFileDescriptorMapper = - ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>; + ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, kLutFileDescriptorMapperSize>; [[nodiscard]] virtual hal::Error acceptChanges() = 0; [[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index db41b9b5fc..14088a6428 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -145,7 +145,7 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId case HotplugEvent::Disconnected: return onHotplugDisconnect(hwcDisplayId); case HotplugEvent::LinkUnstable: - return {}; + return onHotplugLinkTrainingFailure(hwcDisplayId); } } @@ -1068,7 +1068,7 @@ const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGeneri return mSupportedLayerGenericMetadata; } -ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, 20>& +ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, 20>& HWComposer::getLutFileDescriptorMapper() { return mLutFileDescriptorMapper; } @@ -1245,6 +1245,16 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect( return DisplayIdentificationInfo{.id = *displayId}; } +std::optional<DisplayIdentificationInfo> HWComposer::onHotplugLinkTrainingFailure( + hal::HWDisplayId hwcDisplayId) { + const auto displayId = toPhysicalDisplayId(hwcDisplayId); + if (!displayId) { + LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display"); + return {}; + } + return DisplayIdentificationInfo{.id = *displayId}; +} + void HWComposer::loadCapabilities() { static_assert(sizeof(hal::Capability) == sizeof(int32_t), "Capability size has changed"); auto capabilities = mComposer->getCapabilities(); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 2c0aa3d6d5..472411cf99 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -544,6 +544,7 @@ private: std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId); std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId); + std::optional<DisplayIdentificationInfo> onHotplugLinkTrainingFailure(hal::HWDisplayId); bool shouldIgnoreHotplugConnect(hal::HWDisplayId, uint8_t port, bool hasDisplayIdentificationData) const; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 384f7b22c7..56ed11f07a 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -47,7 +47,8 @@ namespace android { -VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId, +VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, + VirtualDisplayIdVariant virtualIdVariant, const sp<IGraphicBufferProducer>& sink, const sp<IGraphicBufferProducer>& bqProducer, const sp<IGraphicBufferConsumer>& bqConsumer, @@ -58,7 +59,7 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId d : ConsumerBase(bqConsumer), #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) mHwc(hwc), - mDisplayId(displayId), + mVirtualIdVariant(virtualIdVariant), mDisplayName(name), mSource{}, mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), @@ -119,7 +120,7 @@ VirtualDisplaySurface::~VirtualDisplaySurface() { } status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) { - if (GpuVirtualDisplayId::tryCast(mDisplayId)) { + if (isBackedByGpu()) { return NO_ERROR; } @@ -133,7 +134,7 @@ status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) { } status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { - if (GpuVirtualDisplayId::tryCast(mDisplayId)) { + if (isBackedByGpu()) { return NO_ERROR; } @@ -181,7 +182,10 @@ status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { } status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) { - if (GpuVirtualDisplayId::tryCast(mDisplayId)) { + const auto halVirtualDisplayId = ftl::match( + mVirtualIdVariant, [](HalVirtualDisplayId id) { return ftl::Optional(id); }, + [](auto) { return ftl::Optional<HalVirtualDisplayId>(); }); + if (!halVirtualDisplayId) { return NO_ERROR; } @@ -212,11 +216,8 @@ status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) { VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(), mOutputProducerSlot, outBuffer.get()); - const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); - LOG_FATAL_IF(!halDisplayId); - // At this point we know the output buffer acquire fence, - // so update HWC state with it. - mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer); + // At this point we know the output buffer acquire fence, so update HWC state with it. + mHwc.setOutputBuffer(*halVirtualDisplayId, mOutputFence, outBuffer); status_t result = NO_ERROR; if (fbBuffer != nullptr) { @@ -227,7 +228,7 @@ status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) { hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot } // TODO: Correctly propagate the dataspace from GL composition - result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer, + result = mHwc.setClientTarget(*halVirtualDisplayId, mFbProducerSlot, mFbFence, hwcBuffer, ui::Dataspace::UNKNOWN, hdrSdrRatio); } @@ -235,8 +236,8 @@ status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) { } void VirtualDisplaySurface::onFrameCommitted() { - const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); - if (!halDisplayId) { + const auto halDisplayId = asHalDisplayId(mVirtualIdVariant); + if (!halDisplayId.has_value()) { return; } @@ -250,8 +251,7 @@ void VirtualDisplaySurface::onFrameCommitted() { Mutex::Autolock lock(mMutex); int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot); VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot); - addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], - retireFence); + addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], retireFence); releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]); } @@ -299,7 +299,7 @@ const sp<Fence>& VirtualDisplaySurface::getClientTargetAcquireFence() const { status_t VirtualDisplaySurface::requestBuffer(int pslot, sp<GraphicBuffer>* outBuf) { - if (GpuVirtualDisplayId::tryCast(mDisplayId)) { + if (isBackedByGpu()) { return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf); } @@ -321,7 +321,7 @@ status_t VirtualDisplaySurface::setAsyncMode(bool async) { status_t VirtualDisplaySurface::dequeueBuffer(Source source, PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) { - LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value()); + LOG_ALWAYS_FATAL_IF(isBackedByGpu()); status_t result = mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight, @@ -373,7 +373,7 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, uint PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { - if (GpuVirtualDisplayId::tryCast(mDisplayId)) { + if (isBackedByGpu()) { return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge, outTimestamps); } @@ -459,7 +459,7 @@ status_t VirtualDisplaySurface::attachBuffer(int*, const sp<GraphicBuffer>&) { status_t VirtualDisplaySurface::queueBuffer(int pslot, const QueueBufferInput& input, QueueBufferOutput* output) { - if (GpuVirtualDisplayId::tryCast(mDisplayId)) { + if (isBackedByGpu()) { return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output); } @@ -517,7 +517,7 @@ status_t VirtualDisplaySurface::queueBuffer(int pslot, status_t VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) { - if (GpuVirtualDisplayId::tryCast(mDisplayId)) { + if (isBackedByGpu()) { return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence); } @@ -621,7 +621,10 @@ void VirtualDisplaySurface::resetPerFrameState() { } status_t VirtualDisplaySurface::refreshOutputBuffer() { - LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value()); + const auto halVirtualDisplayId = ftl::match( + mVirtualIdVariant, [](HalVirtualDisplayId id) { return ftl::Optional(id); }, + [](auto) { return ftl::Optional<HalVirtualDisplayId>(); }); + LOG_ALWAYS_FATAL_IF(!halVirtualDisplayId); if (mOutputProducerSlot >= 0) { mSource[SOURCE_SINK]->cancelBuffer( @@ -640,14 +643,16 @@ status_t VirtualDisplaySurface::refreshOutputBuffer() { // until after GPU calls queueBuffer(). So here we just set the buffer // (for use in HWC prepare) but not the fence; we'll call this again with // the proper fence once we have it. - const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); - LOG_FATAL_IF(!halDisplayId); - result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE, + result = mHwc.setOutputBuffer(*halVirtualDisplayId, Fence::NO_FENCE, mProducerBuffers[mOutputProducerSlot]); return result; } +bool VirtualDisplaySurface::isBackedByGpu() const { + return std::holds_alternative<GpuVirtualDisplayId>(mVirtualIdVariant); +} + // This slot mapping function is its own inverse, so two copies are unnecessary. // Both are kept to make the intent clear where the function is called, and for // the (unlikely) chance that we switch to a different mapping function. diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index 90426f729a..cb65c79152 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -75,7 +75,8 @@ class VirtualDisplaySurface : public compositionengine::DisplaySurface, public BnGraphicBufferProducer, private ConsumerBase { public: - VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink, + VirtualDisplaySurface(HWComposer&, VirtualDisplayIdVariant, + const sp<IGraphicBufferProducer>& sink, const sp<IGraphicBufferProducer>& bqProducer, const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name); @@ -145,6 +146,7 @@ private: void updateQueueBufferOutput(QueueBufferOutput&&); void resetPerFrameState(); status_t refreshOutputBuffer(); + bool isBackedByGpu() const; // Both the sink and scratch buffer pools have their own set of slots // ("source slots", or "sslot"). We have to merge these into the single @@ -159,7 +161,7 @@ private: // Immutable after construction // HWComposer& mHwc; - const VirtualDisplayId mDisplayId; + const VirtualDisplayIdVariant mVirtualIdVariant; const std::string mDisplayName; sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_* uint32_t mDefaultOutputFormat; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 008b0571c3..51d4078987 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -29,7 +29,6 @@ #include <cinttypes> #include <numeric> #include <unordered_set> -#include <vector> #include "../Jank/JankTracker.h" @@ -1005,11 +1004,6 @@ void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, finalizeCurrentDisplayFrame(); } -const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames() - const { - return mPresentFrames; -} - void FrameTimeline::onCommitNotComposited() { SFTRACE_CALL(); std::scoped_lock lock(mMutex); @@ -1530,7 +1524,6 @@ void FrameTimeline::flushPendingPresentFences() { mPendingPresentFences.erase(mPendingPresentFences.begin()); } - mPresentFrames.clear(); for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& pendingPresentFence = mPendingPresentFences[i]; nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; @@ -1544,12 +1537,6 @@ void FrameTimeline::flushPendingPresentFences() { auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); - // Surface frames have been jank classified and can be provided to caller - // to detect if buffer stuffing is occurring. - for (const auto& frame : displayFrame->getSurfaceFrames()) { - mPresentFrames.push_back(frame); - } - mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 9fedb57aca..fa83cd8523 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -331,11 +331,6 @@ public: virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence) = 0; - // Provides surface frames that have already been jank classified in the most recent - // flush of pending present fences. This allows buffer stuffing detection from SF. - virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames() - const = 0; - // Tells FrameTimeline that a frame was committed but not composited. This is used to flush // all the associated surface frames. virtual void onCommitNotComposited() = 0; @@ -513,8 +508,6 @@ public: void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override; - const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames() - const override; void onCommitNotComposited() override; void parseArgs(const Vector<String16>& args, std::string& result) override; void setMaxDisplayFrames(uint32_t size) override; @@ -562,9 +555,6 @@ private: // display frame, this is a good starting size for the vector so that we can avoid the // internal vector resizing that happens with push_back. static constexpr uint32_t kNumSurfaceFramesInitial = 10; - // Presented surface frames that have been jank classified and can - // indicate of potential buffer stuffing. - std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames; }; } // namespace impl diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 1514340e50..964a9703b3 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -41,7 +41,7 @@ void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFram if (forceFullDamage) { outSurfaceDamageRegion = Region::INVALID_REGION; } else { - outSurfaceDamageRegion = requested.surfaceDamageRegion; + outSurfaceDamageRegion = requested.getSurfaceDamageRegion(); } } @@ -305,7 +305,11 @@ std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) { out << rootId << ","; } } - out << "] " << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible") + out << "] "; + if (obj.isSecure) { + out << "(Secure) "; + } + out << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible") << " reason=" << obj.getIsVisibleReason(); if (!obj.geomLayerBounds.isEmpty()) { @@ -372,7 +376,7 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage); if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) { - transparentRegionHint = requested.transparentRegion; + transparentRegionHint = requested.getTransparentRegion(); } if (forceUpdate || requested.what & layer_state_t::eFlagsChanged) { layerOpaqueFlagSet = @@ -444,15 +448,7 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate } if (forceUpdate || requested.what & layer_state_t::eInputInfoChanged) { - if (requested.windowInfoHandle) { - inputInfo = *requested.windowInfoHandle->getInfo(); - } else { - inputInfo = {}; - // b/271132344 revisit this and see if we can always use the layers uid/pid - inputInfo.name = requested.name; - inputInfo.ownerUid = requested.ownerUid; - inputInfo.ownerPid = requested.ownerPid; - } + inputInfo = requested.getWindowInfo(); inputInfo.id = static_cast<int32_t>(uniqueSequence); touchCropId = requested.touchCropId; } @@ -544,7 +540,7 @@ char LayerSnapshot::classifyCompositionForDebug( case Composition::INVALID: return 'i'; case Composition::SOLID_COLOR: - return 'e'; + return 'c'; case Composition::CURSOR: return 'u'; case Composition::SIDEBAND: @@ -552,7 +548,7 @@ char LayerSnapshot::classifyCompositionForDebug( case Composition::DISPLAY_DECORATION: return 'a'; case Composition::REFRESH_RATE_INDICATOR: - return 'f'; + return 'r'; case Composition::CLIENT: case Composition::DEVICE: break; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 86ef6ca61e..28a6031c97 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -1090,7 +1090,7 @@ void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot, snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds); const Rect geomLayerBoundsWithoutTransparentRegion = RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds), - requested.transparentRegion); + requested.getTransparentRegion()); snapshot.transformedBoundsWithoutTransparentRegion = snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion); snapshot.parentTransform = parentSnapshot.geomLayerTransform; @@ -1098,7 +1098,7 @@ void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot, if (requested.potentialCursor) { // Subtract the transparent region and snap to the bounds const Rect bounds = RequestedLayerState::reduce(Rect(snapshot.croppedBufferSize), - requested.transparentRegion); + requested.getTransparentRegion()); snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds); } } @@ -1132,22 +1132,14 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, const Args& args) { using InputConfig = gui::WindowInfo::InputConfig; - if (requested.windowInfoHandle) { - snapshot.inputInfo = *requested.windowInfoHandle->getInfo(); - } else { - snapshot.inputInfo = {}; - // b/271132344 revisit this and see if we can always use the layers uid/pid - snapshot.inputInfo.name = requested.name; - snapshot.inputInfo.ownerUid = gui::Uid{requested.ownerUid}; - snapshot.inputInfo.ownerPid = gui::Pid{requested.ownerPid}; - } + snapshot.inputInfo = requested.getWindowInfo(); snapshot.touchCropId = requested.touchCropId; snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence); snapshot.inputInfo.displayId = ui::LogicalDisplayId{static_cast<int32_t>(snapshot.outputFilter.layerStack.id)}; snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo() - ? requested.windowInfoHandle->getInfo()->touchOcclusionMode + ? requested.getWindowInfo().touchOcclusionMode : parentSnapshot.inputInfo.touchOcclusionMode; snapshot.inputInfo.canOccludePresentation = parentSnapshot.inputInfo.canOccludePresentation || (requested.flags & layer_state_t::eCanOccludePresentation); diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 58c235ed91..621fd6c00e 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -63,8 +63,11 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) metadata.merge(args.metadata); changes |= RequestedLayerState::Changes::Metadata; handleAlive = true; - // TODO: b/305254099 remove once we don't pass invisible windows to input - windowInfoHandle = nullptr; + // b/271132344 revisit this and see if we can always use the layers uid/pid + auto* windowInfo = editWindowInfo(); + windowInfo->name = name; + windowInfo->ownerPid = ownerPid; + windowInfo->ownerUid = ownerUid; if (parentId != UNASSIGNED_LAYER_ID) { canBeRoot = false; } @@ -105,7 +108,7 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) currentHdrSdrRatio = 1.f; dataspaceRequested = false; hdrMetadata.validTypes = 0; - surfaceDamageRegion = Region::INVALID_REGION; + mNotDefCmpState.surfaceDamageRegion = Region::INVALID_REGION; cornerRadius = 0.0f; clientDrawnCornerRadius = 0.0f; backgroundBlurRadius = 0; @@ -147,6 +150,7 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) } void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) { + bool transformWasValid = transformIsValid; const uint32_t oldFlags = flags; const half oldAlpha = color.a; const bool hadBuffer = externalTexture != nullptr; @@ -278,7 +282,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta if (clientState.what & layer_state_t::eReparent) { changes |= RequestedLayerState::Changes::Parent; parentId = resolvedComposerState.parentId; - parentSurfaceControlForChild = nullptr; + mNotDefCmpState.parentSurfaceControlForChild = nullptr; // Once a layer has be reparented, it cannot be placed at the root. It sounds odd // but thats the existing logic and until we make this behavior more explicit, we need // to maintain this logic. @@ -288,7 +292,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta changes |= RequestedLayerState::Changes::RelativeParent; relativeParentId = resolvedComposerState.relativeParentId; isRelativeOf = true; - relativeLayerSurfaceControl = nullptr; + mNotDefCmpState.relativeLayerSurfaceControl = nullptr; } if ((clientState.what & layer_state_t::eLayerChanged || (clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) && @@ -304,7 +308,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta } if (clientState.what & layer_state_t::eInputInfoChanged) { touchCropId = resolvedComposerState.touchCropId; - windowInfoHandle->editInfo()->touchableRegionCropHandle.clear(); + editWindowInfo()->touchableRegionCropHandle.clear(); } if (clientState.what & layer_state_t::eStretchChanged) { stretchEffect.sanitize(); @@ -354,6 +358,14 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta clientDrawnCornerRadius = clientState.clientDrawnCornerRadius; changes |= RequestedLayerState::Changes::Geometry; } + + // We can't just check requestedTransform here because LayerSnapshotBuilder uses + // getTransform which reads destinationFrame or buffer dimensions. + // Display rotation does not affect validity so just use ROT_0. + transformIsValid = LayerSnapshot::isTransformValid(getTransform(ui::Transform::ROT_0)); + if (!transformWasValid && transformIsValid) { + changes |= RequestedLayerState::Changes::Visibility; + } } ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const { @@ -554,12 +566,9 @@ bool RequestedLayerState::hasValidRelativeParent() const { } bool RequestedLayerState::hasInputInfo() const { - if (!windowInfoHandle) { - return false; - } - const auto windowInfo = windowInfoHandle->getInfo(); - return windowInfo->token != nullptr || - windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); + const auto& windowInfo = getWindowInfo(); + return windowInfo.token != nullptr || + windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); } bool RequestedLayerState::needsInputInfo() const { @@ -571,13 +580,9 @@ bool RequestedLayerState::needsInputInfo() const { return true; } - if (!windowInfoHandle) { - return false; - } - - const auto windowInfo = windowInfoHandle->getInfo(); - return windowInfo->token != nullptr || - windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); + const auto& windowInfo = getWindowInfo(); + return windowInfo.token != nullptr || + windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); } bool RequestedLayerState::hasBufferOrSidebandStream() const { diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 723237940d..b8310beab5 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -115,6 +115,7 @@ struct RequestedLayerState : layer_state_t { const gui::Pid ownerPid; bool dataspaceRequested; bool hasColorTransform; + bool transformIsValid = true; bool premultipliedAlpha{true}; // This layer can be a cursor on some displays. bool potentialCursor{false}; diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h index 614f33fbce..758b111e41 100644 --- a/services/surfaceflinger/HdrLayerInfoReporter.h +++ b/services/surfaceflinger/HdrLayerInfoReporter.h @@ -19,11 +19,11 @@ #include <android-base/thread_annotations.h> #include <android/gui/IHdrLayerInfoListener.h> #include <binder/IBinder.h> +#include <ui/RingBuffer.h> #include <utils/Timers.h> #include <unordered_map> -#include "Utils/RingBuffer.h" #include "WpHash.h" namespace android { @@ -102,7 +102,7 @@ private: EventHistoryEntry(const HdrLayerInfo& info) : info(info) { timestamp = systemTime(); } }; - utils::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory; + ui::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory; }; } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index e1bba44be9..2e312827f2 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -362,7 +362,7 @@ aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionT // transaction // ---------------------------------------------------------------------------- -void Layer::commitTransaction() { +void Layer::commitTransaction() REQUIRES(mFlinger->mStateLock) { // Set the present state for all bufferlessSurfaceFramesTX to Presented. The // bufferSurfaceFrameTX will be presented in latchBuffer. for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) { @@ -394,7 +394,8 @@ bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) { }; void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, - nsecs_t postTime, gui::GameMode gameMode) { + nsecs_t postTime, gui::GameMode gameMode) + REQUIRES(mFlinger->mStateLock) { mDrawingState.postTime = postTime; // Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if @@ -458,7 +459,7 @@ void Layer::addSurfaceFrameDroppedForBuffer( void Layer::addSurfaceFramePresentedForBuffer( std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime, - nsecs_t currentLatchTime) { + nsecs_t currentLatchTime) REQUIRES(mFlinger->mStateLock) { surfaceFrame->setAcquireFenceTime(acquireFenceTime); surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime); mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame); @@ -466,7 +467,8 @@ void Layer::addSurfaceFramePresentedForBuffer( } std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction( - const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) { + const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) + REQUIRES(mFlinger->mStateLock) { auto surfaceFrame = mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, getSequence(), mName, @@ -488,7 +490,7 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransac std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName, - gui::GameMode gameMode) { + gui::GameMode gameMode) REQUIRES(mFlinger->mStateLock) { auto surfaceFrame = mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, getSequence(), mName, debugName, @@ -506,7 +508,8 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( } void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime, - std::string debugName, gui::GameMode gameMode) { + std::string debugName, gui::GameMode gameMode) + REQUIRES(mFlinger->mStateLock) { if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { return; } @@ -719,6 +722,10 @@ void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& l uint32_t currentMaxAcquiredBufferCount = mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); + if (FlagManager::getInstance().monitor_buffer_fences()) { + buffer->getDependencyMonitor().addEgress(FenceTime::makeValid(fence), "Layer release"); + } + if (listener) { listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount); } @@ -842,7 +849,7 @@ bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) { return true; } -void Layer::releasePreviousBuffer() { +void Layer::releasePreviousBuffer() REQUIRES(mFlinger->mStateLock) { mReleasePreviousBuffer = true; if (!mBufferInfo.mBuffer || (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) || @@ -884,7 +891,8 @@ void Layer::resetDrawingStateBufferInfo() { bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, - bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) { + bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) + REQUIRES(mFlinger->mStateLock) { SFTRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false")); const bool frameNumberChanged = @@ -936,6 +944,7 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, std::max(mDrawingState.frameNumber, mDrawingState.barrierFrameNumber); mDrawingState.releaseBufferListener = bufferData.releaseBufferListener; + mDrawingState.previousBuffer = std::move(mDrawingState.buffer); mDrawingState.buffer = std::move(buffer); mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) ? bufferData.acquireFence @@ -1074,7 +1083,8 @@ bool Layer::setDesiredHdrHeadroom(float desiredRatio) { } bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info, - nsecs_t postTime, gui::GameMode gameMode) { + nsecs_t postTime, gui::GameMode gameMode) + REQUIRES(mFlinger->mStateLock) { if (mDrawingState.sidebandStream == sidebandStream) return false; if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) { @@ -1117,6 +1127,7 @@ bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence; handle->frameNumber = mDrawingState.frameNumber; handle->previousFrameNumber = mDrawingState.previousFrameNumber; + handle->previousBuffer = mDrawingState.previousBuffer; if (mPreviousReleaseBufferEndpoint == handle->listener) { // Add fence from previous screenshot now so that it can be dispatched to the // client. @@ -1207,7 +1218,7 @@ bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) { return false; } -void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) { +void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) REQUIRES(mFlinger->mStateLock) { const State& s(getDrawingState()); if (!s.buffer) { @@ -1428,8 +1439,8 @@ void Layer::onCompositionPresented(const DisplayDevice* display, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); - } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId()); - displayId && mFlinger->getHwComposer().isConnected(*displayId)) { + } else if (const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant()); + displayId.has_value() && mFlinger->getHwComposer().isConnected(*displayId)) { // The HWC doesn't support present fences, so use the present timestamp instead. const nsecs_t presentTimestamp = mFlinger->getHwComposer().getPresentTimestamp(*displayId); @@ -1457,7 +1468,8 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mBufferInfo.mFrameLatencyNeeded = false; } -bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) { +bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) + REQUIRES(mFlinger->mStateLock) { SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(), getDrawingState().frameNumber); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 6af0f59d51..88754f9fa3 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -117,6 +117,7 @@ public: uint32_t bufferTransform; bool transformToDisplayInverse; Region transparentRegionHint; + std::shared_ptr<renderengine::ExternalTexture> previousBuffer; std::shared_ptr<renderengine::ExternalTexture> buffer; sp<Fence> acquireFence; std::shared_ptr<FenceTime> acquireFenceTime; @@ -288,7 +289,7 @@ public: bool leaveState); inline bool hasTrustedPresentationListener() { - return mTrustedPresentationListener.callbackInterface != nullptr; + return mTrustedPresentationListener.getCallback() != nullptr; } // Sets the masked bits. diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index b6192685ae..5e076bdae4 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -410,6 +410,15 @@ void LayerFE::setReleaseFence(const FenceResult& releaseFence) { if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::FULFILLED) { return; } + + if (releaseFence.has_value()) { + if (FlagManager::getInstance().monitor_buffer_fences()) { + if (auto strongBuffer = mReleasedBuffer.promote()) { + strongBuffer->getDependencyMonitor() + .addAccessCompletion(FenceTime::makeValid(releaseFence.value()), "HWC"); + } + } + } mReleaseFence.set_value(releaseFence); mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::FULFILLED; } @@ -428,6 +437,10 @@ LayerFE::ReleaseFencePromiseStatus LayerFE::getReleaseFencePromiseStatus() { return mReleaseFencePromiseStatus; } +void LayerFE::setReleasedBuffer(sp<GraphicBuffer> buffer) { + mReleasedBuffer = std::move(buffer); +} + void LayerFE::setLastHwcState(const LayerFE::HwcLayerDebugState &state) { mLastHwcState = state; } diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h index a537456beb..b89b6b4b92 100644 --- a/services/surfaceflinger/LayerFE.h +++ b/services/surfaceflinger/LayerFE.h @@ -18,6 +18,7 @@ #include <android/gui/CachingHint.h> #include <gui/LayerMetadata.h> +#include <ui/GraphicBuffer.h> #include <ui/LayerStack.h> #include <ui/PictureProfileHandle.h> @@ -58,6 +59,7 @@ public: ftl::Future<FenceResult> createReleaseFenceFuture() override; void setReleaseFence(const FenceResult& releaseFence) override; LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override; + void setReleasedBuffer(sp<GraphicBuffer> buffer) override; void onPictureProfileCommitted() override; // Used for debugging purposes, e.g. perfetto tracing, dumpsys. @@ -95,6 +97,7 @@ private: std::promise<FenceResult> mReleaseFence; ReleaseFencePromiseStatus mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::UNINITIALIZED; HwcLayerDebugState mLastHwcState; + wp<GraphicBuffer> mReleasedBuffer; }; } // namespace android diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 44cd3194bc..84b1a73e0b 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -447,7 +447,7 @@ void LayerProtoHelper::writeSnapshotToProto(perfetto::protos::LayerProto* layerI } layerInfo->set_type("Layer"); - LayerProtoHelper::writeToProto(requestedState.transparentRegion, + LayerProtoHelper::writeToProto(requestedState.getTransparentRegion(), [&]() { return layerInfo->mutable_transparent_region(); }); layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id); diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h index 38dc11d3bc..81155fd697 100644 --- a/services/surfaceflinger/LayerVector.h +++ b/services/surfaceflinger/LayerVector.h @@ -49,7 +49,8 @@ public: using Visitor = std::function<void(Layer*)>; private: - const StateSet mStateSet; + // FIXME: This is set but not used anywhere. + [[maybe_unused]] const StateSet mStateSet; }; } diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp index cd7210c627..788448d079 100644 --- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp +++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp @@ -515,7 +515,7 @@ void PowerAdvisor::setRequiresRenderEngine(DisplayId displayId, bool requiresRen } void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) { - mExpectedPresentTimes.append(expectedPresentTime); + mExpectedPresentTimes.next() = expectedPresentTime; } void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) { @@ -532,7 +532,7 @@ void PowerAdvisor::setHwcPresentDelayedTime(DisplayId displayId, TimePoint earli } void PowerAdvisor::setCommitStart(TimePoint commitStartTime) { - mCommitStartTimes.append(commitStartTime); + mCommitStartTimes.next() = commitStartTime; } void PowerAdvisor::setCompositeEnd(TimePoint compositeEndTime) { @@ -579,7 +579,7 @@ std::optional<hal::WorkDuration> PowerAdvisor::estimateWorkDuration() { } // Tracks when we finish presenting to hwc - TimePoint estimatedHwcEndTime = mCommitStartTimes[0]; + TimePoint estimatedHwcEndTime = mCommitStartTimes.back(); // How long we spent this frame not doing anything, waiting for fences or vsync Duration idleDuration = 0ns; @@ -643,13 +643,13 @@ std::optional<hal::WorkDuration> PowerAdvisor::estimateWorkDuration() { // Also add the frame delay duration since the target did not move while we were delayed Duration totalDuration = mFrameDelayDuration + std::max(estimatedHwcEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) - - mCommitStartTimes[0]; + mCommitStartTimes.back(); Duration totalDurationWithoutGpu = - mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes[0]; + mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes.back(); // We finish SurfaceFlinger when post-composition finishes, so add that in here Duration flingerDuration = - estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0]; + estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes.back(); Duration estimatedGpuDuration = firstGpuTimeline.has_value() ? estimatedGpuEndTime.value_or(TimePoint{0ns}) - firstGpuTimeline->startTime : Duration::fromNs(0); @@ -661,7 +661,7 @@ std::optional<hal::WorkDuration> PowerAdvisor::estimateWorkDuration() { hal::WorkDuration duration{ .timeStampNanos = TimePoint::now().ns(), .durationNanos = combinedDuration.ns(), - .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(), + .workPeriodStartTimestampNanos = mCommitStartTimes.back().ns(), .cpuDurationNanos = supportsGpuReporting() ? cpuDuration.ns() : 0, .gpuDurationNanos = supportsGpuReporting() ? estimatedGpuDuration.ns() : 0, }; diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h index 540a9df2ca..b97160a3e5 100644 --- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h +++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h @@ -23,6 +23,7 @@ #include <ui/DisplayId.h> #include <ui/FenceTime.h> +#include <ui/RingBuffer.h> #include <utils/Mutex.h> // FMQ library in IPower does questionable conversions @@ -247,27 +248,6 @@ private: std::optional<GpuTimeline> estimateGpuTiming(std::optional<TimePoint> previousEndTime); }; - template <class T, size_t N> - class RingBuffer { - std::array<T, N> elements = {}; - size_t mIndex = 0; - size_t numElements = 0; - - public: - void append(T item) { - mIndex = (mIndex + 1) % N; - numElements = std::min(N, numElements + 1); - elements[mIndex] = item; - } - bool isFull() const { return numElements == N; } - // Allows access like [0] == current, [-1] = previous, etc.. - T& operator[](int offset) { - size_t positiveOffset = - static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N)); - return elements[(mIndex + positiveOffset) % N]; - } - }; - // Filter and sort the display ids by a given property std::vector<DisplayId> getOrderedDisplayIds( std::optional<TimePoint> DisplayTimingData::*sortBy); @@ -287,9 +267,9 @@ private: // Last frame's post-composition duration Duration mLastPostcompDuration{0ns}; // Buffer of recent commit start times - RingBuffer<TimePoint, 2> mCommitStartTimes; + ui::RingBuffer<TimePoint, 2> mCommitStartTimes; // Buffer of recent expected present times - RingBuffer<TimePoint, 2> mExpectedPresentTimes; + ui::RingBuffer<TimePoint, 2> mExpectedPresentTimes; // Most recent present fence time, provided by SF after composition engine finishes presenting TimePoint mLastPresentFenceTime; // Most recent composition engine present end time, returned with the present fence from SF diff --git a/services/surfaceflinger/PowerAdvisor/SessionManager.h b/services/surfaceflinger/PowerAdvisor/SessionManager.h index 93a80b55ab..afa52eb260 100644 --- a/services/surfaceflinger/PowerAdvisor/SessionManager.h +++ b/services/surfaceflinger/PowerAdvisor/SessionManager.h @@ -68,7 +68,8 @@ private: bool isLayerRelevant(int32_t layerId); // The UID of whoever created our ISessionManager connection - const uid_t mUid; + // FIXME: This is set but is not used anywhere. + [[maybe_unused]] const uid_t mUid; // State owned by the main thread @@ -99,4 +100,4 @@ private: }; } // namespace adpf -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 514adac20c..615492a872 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -348,7 +348,7 @@ void RegionSamplingThread::captureSample() { SurfaceFlinger::ScreenshotArgs screenshotArgs; screenshotArgs.captureTypeVariant = displayWeak; - screenshotArgs.displayId = std::nullopt; + screenshotArgs.displayIdVariant = std::nullopt; screenshotArgs.sourceCrop = sampledBounds.isEmpty() ? layerStackSpaceRect : sampledBounds; screenshotArgs.reqSize = sampledBounds.getSize(); screenshotArgs.dataspace = ui::Dataspace::V0_SRGB; diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index c37b9653cb..5390295e9a 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -512,14 +512,6 @@ void EventThread::onModeRejected(PhysicalDisplayId displayId, DisplayModeId mode mCondition.notify_all(); } -// Merge lists of buffer stuffed Uids -void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { - std::lock_guard<std::mutex> lock(mMutex); - for (auto& [uid, count] : bufferStuffedUids) { - mBufferStuffedUids.emplace_or_replace(uid, count); - } -} - void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { DisplayEventConsumers consumers; @@ -761,10 +753,6 @@ void EventThread::generateFrameTimeline(VsyncEventData& outVsyncEventData, nsecs void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, const DisplayEventConsumers& consumers) { - // List of Uids that have been sent vsync data with queued buffer count. - // Used to keep track of which Uids can be removed from the map of - // buffer stuffed clients. - ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers; for (const auto& consumer : consumers) { DisplayEventReceiver::Event copy = event; if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC) { @@ -774,13 +762,6 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, event.vsync.vsyncData.preferredExpectedPresentationTime(), event.vsync.vsyncData.preferredDeadlineTimestamp()); } - auto it = mBufferStuffedUids.find(consumer->mOwnerUid); - if (it != mBufferStuffedUids.end()) { - copy.vsync.vsyncData.numberQueuedBuffers = it->second; - uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid); - } else { - copy.vsync.vsyncData.numberQueuedBuffers = 0; - } switch (consumer->postEvent(copy)) { case NO_ERROR: break; @@ -796,12 +777,6 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, removeDisplayEventConnectionLocked(consumer); } } - // The clients that have already received the queued buffer count - // can be removed from the buffer stuffed Uid list to avoid - // being sent duplicate messages. - for (auto uid : uidsPostedQueuedBuffers) { - mBufferStuffedUids.erase(uid); - } if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC && FlagManager::getInstance().vrr_config()) { mLastCommittedVsyncTime = diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 18bf41643c..612883a88b 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -56,7 +56,6 @@ using gui::VsyncEventData; // --------------------------------------------------------------------------- using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; -using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>; enum class VSyncRequest { None = -2, @@ -141,10 +140,6 @@ public: virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) = 0; - - // An elevated number of queued buffers in the server is detected. This propagates a - // flag to Choreographer indicating that buffer stuffing recovery should begin. - virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); }; struct IEventThreadCallback { @@ -199,8 +194,6 @@ public: void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) override; - void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override; - private: friend EventThreadTest; @@ -241,10 +234,6 @@ private: scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; - // All consumers that need to recover from buffer stuffing and the number - // of their queued buffers. - BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex); - IEventThreadCallback& mCallback; std::thread mThread; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 4da76f6ecc..c9d3b31061 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -554,8 +554,7 @@ void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) { ftl::FakeGuard guard(kMainThreadContext); for (const auto& [id, display] : mDisplays) { - if (display.powerMode != hal::PowerMode::OFF || - !FlagManager::getInstance().multithreaded_present()) { + if (display.powerMode != hal::PowerMode::OFF) { resyncToHardwareVsyncLocked(id, allowToEnable); } } @@ -961,11 +960,6 @@ bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals, return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); } -void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { - if (!mRenderEventThread) return; - mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids)); -} - void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) { std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule; { @@ -986,7 +980,7 @@ std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked( if (const auto pacesetterOpt = pacesetterDisplayLocked()) { const Display& pacesetter = *pacesetterOpt; - if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) { + if (params.toggleIdleTimer) { pacesetter.selectorPtr->setIdleTimerCallbacks( {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); }, .onExpired = [this] { idleTimerCallback(TimerState::Expired); }}, @@ -1018,7 +1012,7 @@ void Scheduler::applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule> vsyncSchedu } void Scheduler::demotePacesetterDisplay(PromotionParams params) { - if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) { + if (params.toggleIdleTimer) { // No need to lock for reads on kMainThreadContext. if (const auto pacesetterPtr = FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 3fdddac52a..61469c1b46 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -337,10 +337,6 @@ public: mPacesetterFrameDurationFractionToSkip = frameDurationFraction; } - // Propagates a flag to the EventThread indicating that buffer stuffing - // recovery should begin. - void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); - void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; } private: @@ -386,7 +382,7 @@ private: // a deadlock where the main thread joins with the timer thread as the timer thread waits to // lock a mutex held by the main thread. struct PromotionParams { - // Whether to stop and start the idle timer. Ignored unless connected_display flag is set. + // Whether to stop and start the idle timer. bool toggleIdleTimer; }; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index ff360b754c..bb04d12d7c 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -206,7 +206,12 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { // Normalizing to the oldest timestamp cuts down on error in calculating the intercept. const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end()); auto it = mRateMap.find(idealPeriod()); - auto const currentPeriod = it->second.slope; + // Calculated slope over the period of time can become outdated as the new timestamps are + // stored. Using idealPeriod instead provides a rate which is valid at all the times. + auto const currentPeriod = + mDisplayModePtr->getVrrConfig() && FlagManager::getInstance().vsync_predictor_recovery() + ? idealPeriod() + : it->second.slope; // The mean of the ordinals must be precise for the intercept calculation, so scale them up for // fixed-point arithmetic. diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h index 813d4dedff..ff461d2d85 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h @@ -24,6 +24,7 @@ #include <ui/DisplayId.h> #include <ui/Fence.h> #include <ui/FenceTime.h> +#include <ui/RingBuffer.h> #include <scheduler/Features.h> #include <scheduler/FrameTime.h> @@ -34,7 +35,6 @@ // TODO(b/185536303): Pull to FTL. #include "../../../TracedOrdinal.h" #include "../../../Utils/Dumper.h" -#include "../../../Utils/RingBuffer.h" namespace android::scheduler { @@ -108,7 +108,7 @@ protected: std::pair<bool /* wouldBackpressure */, PresentFence> expectedSignaledPresentFence( Period vsyncPeriod, Period minFramePeriod) const; std::array<PresentFence, 2> mPresentFencesLegacy; - utils::RingBuffer<PresentFence, 5> mPresentFences; + ui::RingBuffer<PresentFence, 5> mPresentFences; FrameTime mLastSignaledFrameTime; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h index 767462dfce..70ae940b65 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h @@ -36,7 +36,7 @@ enum class CompositionCoverage : std::uint8_t { using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>; -using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayId, CompositionCoverageFlags>; +using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayIdVariant, CompositionCoverageFlags>; inline CompositionCoverageFlags multiDisplayUnion(const CompositionCoveragePerDisplay& displays) { CompositionCoverageFlags coverage; diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp index 712390574f..2906bbd5b8 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.cpp +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -30,11 +30,12 @@ namespace android { std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) { std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated< ScreenCaptureOutput, compositionengine::CompositionEngine, - /* sourceCrop */ const Rect, std::optional<DisplayId>, + /* sourceCrop */ const Rect, ftl::Optional<DisplayIdVariant>, const compositionengine::Output::ColorProfile&, /* layerAlpha */ float, - /* regionSampling */ bool>(args.compositionEngine, args.sourceCrop, args.displayId, - args.colorProfile, args.layerAlpha, args.regionSampling, + /* regionSampling */ bool>(args.compositionEngine, args.sourceCrop, + args.displayIdVariant, args.colorProfile, args.layerAlpha, + args.regionSampling, args.dimInGammaSpaceForEnhancedScreenshots, args.enableLocalTonemapping); output->editState().isSecure = args.isSecure; @@ -59,8 +60,8 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp { std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput"; - if (args.displayId) { - base::StringAppendF(&name, " for %" PRIu64, args.displayId.value().value); + if (const auto id = args.displayIdVariant.and_then(asDisplayIdOfType<DisplayId>)) { + base::StringAppendF(&name, " for %" PRIu64, id->value); } output->setName(name); } @@ -68,12 +69,12 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp } ScreenCaptureOutput::ScreenCaptureOutput( - const Rect sourceCrop, std::optional<DisplayId> displayId, + const Rect sourceCrop, ftl::Optional<DisplayIdVariant> displayIdVariant, const compositionengine::Output::ColorProfile& colorProfile, float layerAlpha, bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping) : mSourceCrop(sourceCrop), - mDisplayId(displayId), + mDisplayIdVariant(displayIdVariant), mColorProfile(colorProfile), mLayerAlpha(layerAlpha), mRegionSampling(regionSampling), @@ -137,12 +138,9 @@ ScreenCaptureOutput::generateLuts() { } std::vector<aidl::android::hardware::graphics::composer3::Luts> luts; - if (mDisplayId) { - const auto id = PhysicalDisplayId::tryCast(mDisplayId.value()); - if (id) { - auto& hwc = getCompositionEngine().getHwComposer(); - hwc.getLuts(*id, buffers, &luts); - } + if (const auto physicalDisplayId = mDisplayIdVariant.and_then(asPhysicalDisplayId)) { + auto& hwc = getCompositionEngine().getHwComposer(); + hwc.getLuts(*physicalDisplayId, buffers, &luts); } if (buffers.size() == luts.size()) { @@ -209,6 +207,7 @@ ScreenCaptureOutput::generateClientCompositionRequests( } compositionengine::LayerFE::LayerSettings fillLayer; + fillLayer.name = "ScreenCaptureFillLayer"; fillLayer.source.buffer.buffer = nullptr; fillLayer.source.solidColor = half3(0.0f, 0.0f, 0.0f); fillLayer.geometry.boundaries = diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h index b3e98b1a32..d4e20fc2f3 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.h +++ b/services/surfaceflinger/ScreenCaptureOutput.h @@ -30,7 +30,7 @@ struct ScreenCaptureOutputArgs { ui::LayerStack layerStack; Rect sourceCrop; std::shared_ptr<renderengine::ExternalTexture> buffer; - std::optional<DisplayId> displayId; + ftl::Optional<DisplayIdVariant> displayIdVariant; ui::Size reqBufferSize; float sdrWhitePointNits; float displayBrightnessNits; @@ -51,7 +51,7 @@ struct ScreenCaptureOutputArgs { // SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay. class ScreenCaptureOutput : public compositionengine::impl::Output { public: - ScreenCaptureOutput(const Rect sourceCrop, std::optional<DisplayId> displayId, + ScreenCaptureOutput(const Rect sourceCrop, ftl::Optional<DisplayIdVariant> displayIdVariant, const compositionengine::Output::ColorProfile& colorProfile, float layerAlpha, bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping); @@ -70,7 +70,7 @@ protected: private: std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> generateLuts(); const Rect mSourceCrop; - const std::optional<DisplayId> mDisplayId; + const ftl::Optional<DisplayIdVariant> mDisplayIdVariant; const compositionengine::Output::ColorProfile& mColorProfile; const float mLayerAlpha; const bool mRegionSampling; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index f3db4c5864..ce7a720714 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -66,13 +66,11 @@ #include <ftl/concat.h> #include <ftl/fake_guard.h> #include <ftl/future.h> -#include <ftl/small_map.h> #include <ftl/unit.h> #include <gui/AidlUtil.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> #include <gui/IProducerListener.h> -#include <gui/JankInfo.h> #include <gui/LayerMetadata.h> #include <gui/LayerState.h> #include <gui/Surface.h> @@ -384,6 +382,7 @@ bool SurfaceFlinger::useHwcForRgbToYuv; bool SurfaceFlinger::hasSyncFramework; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; int64_t SurfaceFlinger::minAcquiredBuffers = 1; +std::optional<int64_t> SurfaceFlinger::maxAcquiredBuffersOpt; uint32_t SurfaceFlinger::maxGraphicsWidth; uint32_t SurfaceFlinger::maxGraphicsHeight; bool SurfaceFlinger::useContextPriority; @@ -452,6 +451,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2); minAcquiredBuffers = SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers); + maxAcquiredBuffersOpt = SurfaceFlingerProperties::max_acquired_buffers(); maxGraphicsWidth = std::max(max_graphics_width(0), 0); maxGraphicsHeight = std::max(max_graphics_height(0), 0); @@ -576,9 +576,10 @@ void SurfaceFlinger::run() { mScheduler->run(); } -sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, bool isSecure, - const std::string& uniqueId, - float requestedRefreshRate) { +sp<IBinder> SurfaceFlinger::createVirtualDisplay( + const std::string& displayName, bool isSecure, + gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId, + float requestedRefreshRate) { // SurfaceComposerAIDL checks for some permissions, but adding an additional check here. // This is to ensure that only root, system, and graphics can request to create a secure // display. Secure displays can show secure content so we add an additional restriction on it. @@ -588,6 +589,8 @@ sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, return nullptr; } + ALOGD("Creating virtual display: %s", displayName.c_str()); + class DisplayToken : public BBinder { sp<SurfaceFlinger> flinger; virtual ~DisplayToken() { @@ -611,6 +614,9 @@ sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, // Set display as protected when marked as secure to ensure no behavior change // TODO (b/314820005): separate as a different arg when creating the display. state.isProtected = isSecure; + state.optimizationPolicy = optimizationPolicy; + // Virtual displays start in ON mode. + state.initialPowerMode = hal::PowerMode::ON; state.displayName = displayName; state.uniqueId = uniqueId; state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate); @@ -632,6 +638,9 @@ status_t SurfaceFlinger::destroyVirtualDisplay(const sp<IBinder>& displayToken) ALOGE("%s: Invalid operation on physical display", __func__); return INVALID_OPERATION; } + + ALOGD("Destroying virtual display: %s", state.displayName.c_str()); + mCurrentState.displays.removeItemsAt(index); setTransactionFlags(eDisplayTransactionNeeded); return NO_ERROR; @@ -648,12 +657,14 @@ void SurfaceFlinger::enableHalVirtualDisplays(bool enable) { } } -VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format, - const std::string& uniqueId) { +std::optional<VirtualDisplayIdVariant> SurfaceFlinger::acquireVirtualDisplay( + ui::Size resolution, ui::PixelFormat format, const std::string& uniqueId, + compositionengine::DisplayCreationArgsBuilder& builder) { if (auto& generator = mVirtualDisplayIdGenerators.hal) { if (const auto id = generator->generateId()) { if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) { acquireVirtualDisplaySnapshot(*id, uniqueId); + builder.setId(*id); return *id; } @@ -668,22 +679,23 @@ VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui:: const auto id = mVirtualDisplayIdGenerators.gpu.generateId(); LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display"); acquireVirtualDisplaySnapshot(*id, uniqueId); + builder.setId(*id); return *id; } -void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) { - if (const auto id = HalVirtualDisplayId::tryCast(displayId)) { - if (auto& generator = mVirtualDisplayIdGenerators.hal) { - generator->releaseId(*id); - releaseVirtualDisplaySnapshot(*id); - } - return; - } - - const auto id = GpuVirtualDisplayId::tryCast(displayId); - LOG_ALWAYS_FATAL_IF(!id); - mVirtualDisplayIdGenerators.gpu.releaseId(*id); - releaseVirtualDisplaySnapshot(*id); +void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayIdVariant displayId) { + ftl::match( + displayId, + [this](HalVirtualDisplayId halVirtualDisplayId) { + if (auto& generator = mVirtualDisplayIdGenerators.hal) { + generator->releaseId(halVirtualDisplayId); + releaseVirtualDisplaySnapshot(halVirtualDisplayId); + } + }, + [this](GpuVirtualDisplayId gpuVirtualDisplayId) { + mVirtualDisplayIdGenerators.gpu.releaseId(gpuVirtualDisplayId); + releaseVirtualDisplaySnapshot(gpuVirtualDisplayId); + }); } void SurfaceFlinger::releaseVirtualDisplaySnapshot(VirtualDisplayId displayId) { @@ -1009,9 +1021,8 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { mPowerAdvisor->init(); if (base::GetBoolProperty("service.sf.prime_shader_cache"s, true)) { - if (setSchedFifo(false) != NO_ERROR) { - ALOGW("Can't set SCHED_OTHER for primeCache"); - } + constexpr const char* kWhence = "primeCache"; + setSchedFifo(false, kWhence); mRenderEnginePrimeCacheFuture.callOnce([this] { renderengine::PrimeCacheConfig config; @@ -1047,9 +1058,7 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { return getRenderEngine().primeCache(config); }); - if (setSchedFifo(true) != NO_ERROR) { - ALOGW("Can't set SCHED_FIFO after primeCache"); - } + setSchedFifo(true, kWhence); } // Avoid blocking the main thread on `init` to set properties. @@ -1174,6 +1183,7 @@ status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDispl const auto& snapshot = snapshotRef.get(); info->connectionType = snapshot.connectionType(); + info->port = snapshot.port(); info->deviceProductInfo = snapshot.deviceProductInfo(); if (mEmulatedDisplayDensity) { @@ -1257,7 +1267,17 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue()); info->frameRateCategoryRate = frameRateCategoryRate; - info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates(); + if (info->hasArrSupport) { + info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates(); + } else { + // On non-ARR devices, list the refresh rates same as the supported display modes. + std::vector<float> supportedFrameRates; + supportedFrameRates.reserve(info->supportedDisplayModes.size()); + std::transform(info->supportedDisplayModes.begin(), info->supportedDisplayModes.end(), + std::back_inserter(supportedFrameRates), + [](ui::DisplayMode mode) { return mode.peakRefreshRate; }); + info->supportedRefreshRates = supportedFrameRates; + } info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities()); @@ -2286,8 +2306,7 @@ void SurfaceFlinger::scheduleSample() { void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { - if (FlagManager::getInstance().connected_display() && timestamp < 0 && - vsyncPeriod.has_value()) { + if (timestamp < 0 && vsyncPeriod.has_value()) { 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 @@ -2339,9 +2358,19 @@ void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, return; } - if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE && - !FlagManager::getInstance().display_config_error_hal()) { - return; + if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE) { + if (!FlagManager::getInstance().display_config_error_hal()) { + return; + } + { + std::lock_guard<std::mutex> lock(mHotplugMutex); + mPendingHotplugEvents.push_back( + HotplugEvent{hwcDisplayId, HWComposer::HotplugEvent::LinkUnstable}); + } + if (mScheduler) { + mScheduler->scheduleConfigure(); + } + // do not return to also report the error. } // TODO(b/311403559): use enum type instead of int @@ -2483,7 +2512,8 @@ void SurfaceFlinger::updateLayerHistory(nsecs_t now) { } bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, - bool flushTransactions, bool& outTransactionsAreEmpty) { + bool flushTransactions, bool& outTransactionsAreEmpty) + EXCLUDES(mStateLock) { using Changes = frontend::RequestedLayerState::Changes; SFTRACE_CALL(); SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Transaction Handling"); @@ -2680,7 +2710,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, } bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, - const scheduler::FrameTargets& frameTargets) { + const scheduler::FrameTargets& frameTargets) EXCLUDES(mStateLock) { const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get(); const VsyncId vsyncId = pacesetterFrameTarget.vsyncId(); @@ -2845,9 +2875,9 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( // output. Layer stacks are not tracked in Display when we iterate through // frameTargeters. Cross-referencing layer stacks allows us to filter out displays // by ID with duplicate layer stacks before adding them to CompositionEngine output. - ui::DisplayMap<DisplayId, ui::LayerStack> physicalDisplayLayerStacks; + ui::DisplayMap<PhysicalDisplayId, ui::LayerStack> physicalDisplayLayerStacks; for (auto& [_, display] : displays) { - const auto id = PhysicalDisplayId::tryCast(display->getId()); + const auto id = asPhysicalDisplayId(display->getDisplayIdVariant()); if (id && frameTargeters.contains(*id)) { physicalDisplayLayerStacks.try_emplace(*id, display->getLayerStack()); } @@ -2855,18 +2885,20 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( // Tracks layer stacks of displays that are added to CompositionEngine output. ui::DisplayMap<ui::LayerStack, ftl::Unit> outputLayerStacks; - auto isOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) { - if (FlagManager::getInstance().reject_dupe_layerstacks() && - outputLayerStacks.contains(layerStack)) { - // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is - // removed - ALOGD("Existing layer stack ID %d output to another display %" PRIu64 - ", dropping display from outputs", - layerStack.id, id.value); - return true; + auto isUniqueOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) { + if (FlagManager::getInstance().reject_dupe_layerstacks()) { + if (layerStack != ui::INVALID_LAYER_STACK && outputLayerStacks.contains(layerStack)) { + // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is + // removed + ALOGD("Existing layer stack ID %d output to another display %" PRIu64 + ", dropping display from outputs", + layerStack.id, id.value); + return false; + } } + outputLayerStacks.try_emplace(layerStack); - return false; + return true; }; // Add outputs for physical displays. @@ -2875,7 +2907,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( if (const auto display = getCompositionDisplayLocked(id)) { const auto layerStack = physicalDisplayLayerStacks.get(id)->get(); - if (!isOutputLayerStack(display->getId(), layerStack)) { + if (isUniqueOutputLayerStack(display->getId(), layerStack)) { refreshArgs.outputs.push_back(display); } } @@ -2894,7 +2926,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( if (!refreshRate.isValid() || mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) { - if (!isOutputLayerStack(display->getId(), display->getLayerStack())) { + if (isUniqueOutputLayerStack(display->getId(), display->getLayerStack())) { refreshArgs.outputs.push_back(display->getCompositionDisplay()); } } @@ -3111,7 +3143,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( for (const auto& [_, display] : displays) { const auto& state = display->getCompositionDisplay()->getState(); CompositionCoverageFlags& flags = - mCompositionCoverage.try_emplace(display->getId()).first->second; + mCompositionCoverage.try_emplace(display->getDisplayIdVariant()).first->second; if (state.usesDeviceComposition) { flags |= CompositionCoverage::Hwc; @@ -3165,8 +3197,8 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( CompositeResultsPerDisplay resultsPerDisplay; // Filter out virtual displays. - for (const auto& [id, coverage] : mCompositionCoverage) { - if (const auto idOpt = PhysicalDisplayId::tryCast(id)) { + for (const auto& [idVar, coverage] : mCompositionCoverage) { + if (const auto idOpt = asPhysicalDisplayId(idVar)) { resultsPerDisplay.try_emplace(*idOpt, CompositeResult{coverage}); } } @@ -3204,16 +3236,12 @@ bool SurfaceFlinger::isHdrLayer(const frontend::LayerSnapshot& snapshot) const { return false; } -ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId, +ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(PhysicalDisplayId displayId, bool isPrimary) const { - const auto id = PhysicalDisplayId::tryCast(displayId); - if (!id) { - return ui::ROTATION_0; - } if (!mIgnoreHwcPhysicalDisplayOrientation && getHwComposer().getComposer()->isSupported( Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) { - switch (getHwComposer().getPhysicalDisplayOrientation(*id)) { + switch (getHwComposer().getPhysicalDisplayOrientation(displayId)) { case Hwc2::AidlTransform::ROT_90: return ui::ROTATION_90; case Hwc2::AidlTransform::ROT_180: @@ -3285,40 +3313,12 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const TimePoint presentTime = TimePoint::now(); - // The Uids of layer owners that are in buffer stuffing mode, and their elevated - // buffer counts. Messages to start recovery are sent exclusively to these Uids. - BufferStuffingMap bufferStuffedUids; - // Set presentation information before calling Layer::releasePendingBuffer, such that jank // information from previous' frame classification is already available when sending jank info // to clients, so they get jank classification as early as possible. mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime, pacesetterGpuCompositionDoneFenceTime); - // Find and register any layers that are in buffer stuffing mode - const auto& presentFrames = mFrameTimeline->getPresentFrames(); - - for (const auto& frame : presentFrames) { - const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId()); - if (!layer) continue; - uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0; - int32_t jankType = frame->getJankType().value_or(JankType::None); - if (jankType & JankType::BufferStuffing && - layer->flags & layer_state_t::eRecoverableFromBufferStuffing) { - auto [it, wasEmplaced] = - bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers); - // Update with maximum number of queued buffers, allows clients drawing - // multiple windows to account for the most severely stuffed window - if (!wasEmplaced && it->second < numberQueuedBuffers) { - it->second = numberQueuedBuffers; - } - } - } - - if (!bufferStuffedUids.empty()) { - mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids)); - } - // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might // be sampled a little later than when we started doing work for this frame, // but that should be okay since CompositorTiming has snapping logic. @@ -3551,9 +3551,8 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( std::vector<HWComposer::HWCDisplayMode> hwcModes; std::optional<hal::HWConfigId> activeModeHwcIdOpt; - const bool isExternalDisplay = FlagManager::getInstance().connected_display() && - getHwComposer().getDisplayConnectionType(displayId) == - ui::DisplayConnectionType::External; + const bool isExternalDisplay = getHwComposer().getDisplayConnectionType(displayId) == + ui::DisplayConnectionType::External; int attempt = 0; constexpr int kMaxAttempts = 3; @@ -3716,11 +3715,12 @@ bool SurfaceFlinger::configureLocked() { const auto displayId = info->id; const ftl::Concat displayString("display ", displayId.value, "(HAL ID ", hwcDisplayId, ')'); - - if (event == HWComposer::HotplugEvent::Connected) { + // TODO: b/393126541 - replace if with switch as all cases are handled. + if (event == HWComposer::HotplugEvent::Connected || + event == HWComposer::HotplugEvent::LinkUnstable) { const auto activeModeIdOpt = processHotplugConnect(displayId, hwcDisplayId, std::move(*info), - displayString.c_str()); + displayString.c_str(), event); if (!activeModeIdOpt) { mScheduler->dispatchHotplugError( static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN)); @@ -3746,7 +3746,7 @@ bool SurfaceFlinger::configureLocked() { LOG_ALWAYS_FATAL_IF(!snapshotOpt); mDisplayModeController.registerDisplay(*snapshotOpt, *activeModeIdOpt, config); - } else { + } else { // event == HWComposer::HotplugEvent::Disconnected // Unregister before destroying the DisplaySnapshot below. mDisplayModeController.unregisterDisplay(displayId); @@ -3761,7 +3761,8 @@ bool SurfaceFlinger::configureLocked() { std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDisplayId displayId, hal::HWDisplayId hwcDisplayId, DisplayIdentificationInfo&& info, - const char* displayString) { + const char* displayString, + HWComposer::HotplugEvent event) { auto [displayModes, activeMode] = loadDisplayModes(displayId); if (!activeMode) { ALOGE("Failed to hotplug %s", displayString); @@ -3796,6 +3797,9 @@ std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDispl state.physical->port = port; ALOGI("Reconnecting %s", displayString); return activeModeId; + } else if (event == HWComposer::HotplugEvent::LinkUnstable) { + ALOGE("Failed to reconnect unknown %s", displayString); + return std::nullopt; } const sp<IBinder> token = sp<BBinder>::make(); @@ -3857,13 +3861,17 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( creationArgs.hasWideColorGamut = false; creationArgs.supportedPerFrameMetadata = 0; - if (const auto physicalIdOpt = PhysicalDisplayId::tryCast(compositionDisplay->getId())) { + if (const auto physicalIdOpt = + compositionDisplay->getDisplayIdVariant().and_then(asPhysicalDisplayId)) { const auto physicalId = *physicalIdOpt; creationArgs.isPrimary = physicalId == getPrimaryDisplayIdLocked(); creationArgs.refreshRateSelector = FTL_FAKE_GUARD(kMainThreadContext, mDisplayModeController.selectorPtrFor(physicalId)); + creationArgs.physicalOrientation = + getPhysicalDisplayOrientation(physicalId, creationArgs.isPrimary); + ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation)); mPhysicalDisplays.get(physicalId) .transform(&PhysicalDisplay::snapshotRef) @@ -3876,7 +3884,8 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( })); } - if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) { + if (const auto id = compositionDisplay->getDisplayIdVariant().and_then( + asHalDisplayId<DisplayIdVariant>)) { getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities); creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id); } @@ -3892,11 +3901,12 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( nativeWindow->setSwapInterval(nativeWindow.get(), 0); } - creationArgs.physicalOrientation = - getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary); - ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation)); - - creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF; + if (FlagManager::getInstance().correct_virtual_display_power_state()) { + creationArgs.initialPowerMode = state.initialPowerMode; + } else { + creationArgs.initialPowerMode = + state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF; + } creationArgs.requestedRefreshRate = state.requestedRefreshRate; @@ -3920,10 +3930,12 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( mode.getPeakFps()); } - display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack)); + display->setLayerFilter( + makeLayerFilterForDisplay(display->getDisplayIdVariant(), state.layerStack)); display->setProjection(state.orientation, state.layerStackSpaceRect, state.orientedDisplaySpaceRect); display->setDisplayName(state.displayName); + display->setOptimizationPolicy(state.optimizationPolicy); display->setFlags(state.flags); return display; @@ -3969,14 +3981,18 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, // Virtual displays without a surface are dormant: // they have external state (layer stack, projection, // etc.) but no internal state (i.e. a DisplayDevice). + ALOGD("Not adding dormant virtual display with token %p: %s", displayToken.unsafe_get(), + state.displayName.c_str()); return; } compositionengine::DisplayCreationArgsBuilder builder; + std::optional<VirtualDisplayIdVariant> virtualDisplayIdVariantOpt; if (const auto& physical = state.physical) { builder.setId(physical->id); } else { - builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId)); + virtualDisplayIdVariantOpt = + acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId, builder); } builder.setPixels(resolution); @@ -3996,10 +4012,10 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false); if (state.isVirtual()) { - const auto displayId = VirtualDisplayId::tryCast(compositionDisplay->getId()); - LOG_FATAL_IF(!displayId); - auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *displayId, state.surface, - bqProducer, bqConsumer, state.displayName); + LOG_FATAL_IF(!virtualDisplayIdVariantOpt); + auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *virtualDisplayIdVariantOpt, + state.surface, bqProducer, bqConsumer, + state.displayName); displaySurface = surface; producer = std::move(surface); } else { @@ -4007,18 +4023,17 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, "adding a supported display, but rendering " "surface is provided (%p), ignoring it", state.surface.get()); - const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId()); - LOG_FATAL_IF(!displayId); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) const auto frameBufferSurface = - sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqProducer, bqConsumer, + sp<FramebufferSurface>::make(getHwComposer(), state.physical->id, bqProducer, + bqConsumer, state.physical->activeMode->getResolution(), ui::Size(maxGraphicsWidth, maxGraphicsHeight)); displaySurface = frameBufferSurface; producer = frameBufferSurface->getSurface()->getIGraphicBufferProducer(); #else displaySurface = - sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer, + sp<FramebufferSurface>::make(getHwComposer(), state.physical->id, bqConsumer, state.physical->activeMode->getResolution(), ui::Size(maxGraphicsWidth, maxGraphicsHeight)); producer = bqProducer; @@ -4043,13 +4058,16 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, incRefreshableDisplays(); } + if (FlagManager::getInstance().correct_virtual_display_power_state()) { + applyOptimizationPolicy(__func__); + } + 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()) { + if (const auto& physical = state.physical; mScheduler && physical) { const bool isInternalDisplay = mPhysicalDisplays.get(physical->id) .transform(&PhysicalDisplay::isInternal) .value_or(false); @@ -4072,8 +4090,8 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { if (display) { display->disconnect(); - if (display->isVirtual()) { - releaseVirtualDisplay(display->getVirtualId()); + if (const auto virtualDisplayIdVariant = display->getVirtualDisplayIdVariant()) { + releaseVirtualDisplay(*virtualDisplayIdVariant); } else { mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId); } @@ -4100,6 +4118,10 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { // not be accessible. })); } + + if (FlagManager::getInstance().correct_virtual_display_power_state()) { + applyOptimizationPolicy(__func__); + } } void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, @@ -4112,8 +4134,8 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) { if (const auto display = getDisplayDeviceLocked(displayToken)) { display->disconnect(); - if (display->isVirtual()) { - releaseVirtualDisplay(display->getVirtualId()); + if (const auto virtualDisplayIdVariant = display->getVirtualDisplayIdVariant()) { + releaseVirtualDisplay(*virtualDisplayIdVariant); } if (display->isRefreshable()) { @@ -4133,7 +4155,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, if (currentState.physical) { const auto display = getDisplayDeviceLocked(displayToken); if (!mSkipPowerOnForQuiescent) { - setPowerModeInternal(display, hal::PowerMode::ON); + setPhysicalDisplayPowerMode(display, hal::PowerMode::ON); } if (display->getPhysicalId() == mActiveDisplayId) { @@ -4145,8 +4167,8 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, if (const auto display = getDisplayDeviceLocked(displayToken)) { if (currentState.layerStack != drawingState.layerStack) { - display->setLayerFilter( - makeLayerFilterForDisplay(display->getId(), currentState.layerStack)); + display->setLayerFilter(makeLayerFilterForDisplay(display->getDisplayIdVariant(), + currentState.layerStack)); } if (currentState.flags != drawingState.flags) { display->setFlags(currentState.flags); @@ -4317,20 +4339,19 @@ void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) { std::move(displayInfos), ftl::to_underlying(vsyncId), frameTime.ns()}, - std::move( - inputWindowCommands.windowInfosReportedListeners), + std::move(inputWindowCommands.releaseListeners()), /* forceImmediateCall= */ visibleWindowsChanged || - !inputWindowCommands.focusRequests.empty()); + !inputWindowCommands.getFocusRequests().empty()); } else { // If there are listeners but no changes to input windows, call the listeners // immediately. - for (const auto& listener : inputWindowCommands.windowInfosReportedListeners) { + for (const auto& listener : inputWindowCommands.getListeners()) { if (IInterface::asBinder(listener)->isBinderAlive()) { listener->onWindowInfosReported(); } } } - for (const auto& focusRequest : inputWindowCommands.focusRequests) { + for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) { inputFlinger->setFocusedWindow(focusRequest); } }}); @@ -4389,7 +4410,7 @@ void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, void SurfaceFlinger::updateCursorAsync() { compositionengine::CompositionRefreshArgs refreshArgs; for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { - if (HalDisplayId::tryCast(display->getId())) { + if (asHalDisplayId(display->getDisplayIdVariant())) { refreshArgs.outputs.push_back(display->getCompositionDisplay()); } } @@ -4893,12 +4914,14 @@ bool SurfaceFlinger::flushTransactionQueues() { return applyTransactions(transactions); } -bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions) { +bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions) + EXCLUDES(mStateLock) { Mutex::Autolock lock(mStateLock); return applyTransactionsLocked(transactions); } -bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions) { +bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions) + REQUIRES(mStateLock) { bool needsTraversal = false; // Now apply all transactions. for (auto& transaction : transactions) { @@ -5051,20 +5074,26 @@ status_t SurfaceFlinger::setTransactionState( layerName.c_str(), transactionId); if (resolvedState.externalTexture) { resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer(); + if (FlagManager::getInstance().monitor_buffer_fences()) { + resolvedState.state.bufferData->buffer->getDependencyMonitor() + .addIngress(FenceTime::makeValid( + resolvedState.state.bufferData->acquireFence), + "Incoming txn"); + } } mBufferCountTracker.increment(resolvedState.layerId); } if (resolvedState.state.what & layer_state_t::eReparent) { - resolvedState.parentId = - getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild); + resolvedState.parentId = getLayerIdFromSurfaceControl( + resolvedState.state.getParentSurfaceControlForChild()); } if (resolvedState.state.what & layer_state_t::eRelativeLayerChanged) { - resolvedState.relativeParentId = - getLayerIdFromSurfaceControl(resolvedState.state.relativeLayerSurfaceControl); + resolvedState.relativeParentId = getLayerIdFromSurfaceControl( + resolvedState.state.getRelativeLayerSurfaceControl()); } if (resolvedState.state.what & layer_state_t::eInputInfoChanged) { wp<IBinder>& touchableRegionCropHandle = - resolvedState.state.windowInfoHandle->editInfo()->touchableRegionCropHandle; + resolvedState.state.editWindowInfo()->touchableRegionCropHandle; resolvedState.touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote()); } @@ -5115,15 +5144,13 @@ status_t SurfaceFlinger::setTransactionState( return NO_ERROR; } -bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo, - std::vector<ResolvedComposerState>& states, - Vector<DisplayState>& displays, uint32_t flags, - const InputWindowCommands& inputWindowCommands, - const int64_t desiredPresentTime, bool isAutoTimestamp, - const std::vector<uint64_t>& uncacheBufferIds, - const int64_t postTime, bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks, - int originPid, int originUid, uint64_t transactionId) { +bool SurfaceFlinger::applyTransactionState( + const FrameTimelineInfo& frameTimelineInfo, std::vector<ResolvedComposerState>& states, + Vector<DisplayState>& displays, uint32_t flags, + const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, + bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds, const int64_t postTime, + bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, + int originPid, int originUid, uint64_t transactionId) REQUIRES(mStateLock) { uint32_t transactionFlags = 0; // start and end registration for listeners w/ no surface so they can get their callback. Note @@ -5275,7 +5302,7 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f ResolvedComposerState& composerState, int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, - uint64_t transactionId) { + uint64_t transactionId) REQUIRES(mStateLock) { layer_state_t& s = composerState.state; std::vector<ListenerCallbacks> filteredListeners; @@ -5625,7 +5652,7 @@ void SurfaceFlinger::initializeDisplays() { // In case of a restart, ensure all displays are off. for (const auto& [id, display] : mPhysicalDisplays) { - setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::OFF); + setPhysicalDisplayPowerMode(getDisplayDeviceLocked(id), hal::PowerMode::OFF); } // Power on all displays. The primary display is first, so becomes the active display. Also, @@ -5634,13 +5661,14 @@ void SurfaceFlinger::initializeDisplays() { // Additionally, do not turn on displays if the boot should be quiescent. if (!mSkipPowerOnForQuiescent) { for (const auto& [id, display] : mPhysicalDisplays) { - setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON); + setPhysicalDisplayPowerMode(getDisplayDeviceLocked(id), hal::PowerMode::ON); } } } } -void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) { +void SurfaceFlinger::setPhysicalDisplayPowerMode(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. @@ -5649,7 +5677,7 @@ 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()); + ALOGD("Setting power mode %d on physical display %s", mode, to_string(displayId).c_str()); const auto currentMode = display->getPowerMode(); if (currentMode == mode) { @@ -5676,7 +5704,13 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: incRefreshableDisplays(); } + if (displayId == mActiveDisplayId && + FlagManager::getInstance().correct_virtual_display_power_state()) { + applyOptimizationPolicy(__func__); + } + const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr; + using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy; if (currentMode == hal::PowerMode::OFF) { // Turn on the display @@ -5691,27 +5725,20 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: onActiveDisplayChangedLocked(activeDisplay.get(), *display); } - if (displayId == mActiveDisplayId) { - // TODO(b/281692563): Merge the syscalls. For now, keep uclamp in a separate syscall and - // set it before SCHED_FIFO due to b/190237315. - if (setSchedAttr(true) != NO_ERROR) { - ALOGW("Failed to set uclamp.min after powering on active display: %s", - strerror(errno)); - } - if (setSchedFifo(true) != NO_ERROR) { - ALOGW("Failed to set SCHED_FIFO after powering on active display: %s", - strerror(errno)); - } + if (displayId == mActiveDisplayId && + !FlagManager::getInstance().correct_virtual_display_power_state()) { + optimizeThreadScheduling("setPhysicalDisplayPowerMode(ON/DOZE)", + OptimizationPolicy::optimizeForPerformance); } getHwComposer().setPowerMode(displayId, mode); - if (mode != hal::PowerMode::DOZE_SUSPEND && - (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) { + if (mode != hal::PowerMode::DOZE_SUSPEND) { const bool enable = mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState(); requestHardwareVsync(displayId, enable); - if (displayId == mActiveDisplayId) { + if (displayId == mActiveDisplayId && + !FlagManager::getInstance().correct_virtual_display_power_state()) { mScheduler->enableSyntheticVsync(false); } @@ -5728,24 +5755,18 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (const auto display = getActivatableDisplay()) { onActiveDisplayChangedLocked(activeDisplay.get(), *display); } else { - if (setSchedFifo(false) != NO_ERROR) { - ALOGW("Failed to set SCHED_OTHER after powering off active display: %s", - strerror(errno)); - } - if (setSchedAttr(false) != NO_ERROR) { - ALOGW("Failed set uclamp.min after powering off active display: %s", - strerror(errno)); + if (!FlagManager::getInstance().correct_virtual_display_power_state()) { + optimizeThreadScheduling("setPhysicalDisplayPowerMode(OFF)", + OptimizationPolicy::optimizeForPower); } - if (currentModeNotDozeSuspend) { - if (!FlagManager::getInstance().multithreaded_present()) { - mScheduler->disableHardwareVsync(displayId, true); - } + if (currentModeNotDozeSuspend && + !FlagManager::getInstance().correct_virtual_display_power_state()) { mScheduler->enableSyntheticVsync(); } } } - if (currentModeNotDozeSuspend && FlagManager::getInstance().multithreaded_present()) { + if (currentModeNotDozeSuspend) { constexpr bool kDisallow = true; mScheduler->disableHardwareVsync(displayId, kDisallow); } @@ -5763,24 +5784,25 @@ 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 (currentMode == hal::PowerMode::DOZE_SUSPEND && - (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) { + if (currentMode == hal::PowerMode::DOZE_SUSPEND) { if (displayId == mActiveDisplayId) { ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON."); mVisibleRegionsDirty = true; scheduleRepaint(); - mScheduler->enableSyntheticVsync(false); + if (!FlagManager::getInstance().correct_virtual_display_power_state()) { + mScheduler->enableSyntheticVsync(false); + } } constexpr bool kAllowToEnable = true; mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get()); } } else if (mode == hal::PowerMode::DOZE_SUSPEND) { // Leave display going to doze - if (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present()) { - constexpr bool kDisallow = true; - mScheduler->disableHardwareVsync(displayId, kDisallow); - } - if (displayId == mActiveDisplayId) { + constexpr bool kDisallow = true; + mScheduler->disableHardwareVsync(displayId, kDisallow); + + if (displayId == mActiveDisplayId && + !FlagManager::getInstance().correct_virtual_display_power_state()) { mScheduler->enableSyntheticVsync(); } getHwComposer().setPowerMode(displayId, mode); @@ -5796,21 +5818,97 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mScheduler->setDisplayPowerMode(displayId, mode); - ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str()); + ALOGD("Finished setting power mode %d on physical display %s", mode, + to_string(displayId).c_str()); +} + +void SurfaceFlinger::setVirtualDisplayPowerMode(const sp<DisplayDevice>& display, + hal::PowerMode mode) { + if (!display->isVirtual()) { + ALOGE("%s: Invalid operation on physical display", __func__); + return; + } + + const auto displayId = display->getVirtualId(); + ALOGD("Setting power mode %d on virtual display %s %s", mode, to_string(displayId).c_str(), + display->getDisplayName().c_str()); + + display->setPowerMode(static_cast<hal::PowerMode>(mode)); + + applyOptimizationPolicy(__func__); + + ALOGD("Finished setting power mode %d on virtual display %s", mode, + to_string(displayId).c_str()); +} + +void SurfaceFlinger::optimizeThreadScheduling( + const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) { + ALOGD("%s: Optimizing thread scheduling: %s", whence, to_string(optimizationPolicy)); + + const bool optimizeForPerformance = + optimizationPolicy == gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance; + // TODO: b/281692563 - Merge the syscalls. For now, keep uclamp in a separate syscall + // and set it before SCHED_FIFO due to b/190237315. + setSchedAttr(optimizeForPerformance, whence); + setSchedFifo(optimizeForPerformance, whence); +} + +void SurfaceFlinger::applyOptimizationPolicy(const char* whence) { + using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy; + + const bool optimizeForPerformance = + std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) { + const auto& display = pair.second; + return display->isPoweredOn() && + display->getOptimizationPolicy() == + OptimizationPolicy::optimizeForPerformance; + }); + + optimizeThreadScheduling(whence, + optimizeForPerformance ? OptimizationPolicy::optimizeForPerformance + : OptimizationPolicy::optimizeForPower); + + if (mScheduler) { + const bool disableSyntheticVsync = + std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) { + const auto& display = pair.second; + const hal::PowerMode powerMode = display->getPowerMode(); + return powerMode != hal::PowerMode::OFF && + powerMode != hal::PowerMode::DOZE_SUSPEND && + display->getOptimizationPolicy() == + OptimizationPolicy::optimizeForPerformance; + }); + mScheduler->enableSyntheticVsync(!disableSyntheticVsync); + } } void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { - auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( - kMainThreadContext) { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { mSkipPowerOnForQuiescent = false; - const auto display = getDisplayDeviceLocked(displayToken); + const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken)); if (!display) { - ALOGE("Attempt to set power mode %d for invalid display token %p", mode, - displayToken.get()); + Mutex::Autolock lock(mStateLock); + const ssize_t index = mCurrentState.displays.indexOfKey(displayToken); + if (index >= 0) { + auto& state = mCurrentState.displays.editValueFor(displayToken); + if (state.isVirtual()) { + ALOGD("Setting power mode %d for a dormant virtual display with token %p", mode, + displayToken.get()); + state.initialPowerMode = static_cast<hal::PowerMode>(mode); + return; + } + } + ALOGE("Failed to set power mode %d for display token %p", mode, displayToken.get()); } else if (display->isVirtual()) { - ALOGW("Attempt to set power mode %d for virtual display", mode); + if (FlagManager::getInstance().correct_virtual_display_power_state()) { + ftl::FakeGuard guard(mStateLock); + setVirtualDisplayPowerMode(display, static_cast<hal::PowerMode>(mode)); + } else { + ALOGW("Attempt to set power mode %d for virtual display", mode); + } } else { - setPowerModeInternal(display, static_cast<hal::PowerMode>(mode)); + ftl::FakeGuard guard(mStateLock); + setPhysicalDisplayPowerMode(display, static_cast<hal::PowerMode>(mode)); } }); @@ -6004,17 +6102,14 @@ void SurfaceFlinger::dumpDisplays(std::string& result) const { for (const auto& [token, display] : mDisplays) { if (display->isVirtual()) { - const auto displayId = display->getId(); + const VirtualDisplayId virtualId = display->getVirtualId(); utils::Dumper::Section section(dumper, - ftl::Concat("Virtual Display ", displayId.value).str()); + ftl::Concat("Virtual Display ", virtualId.value).str()); display->dump(dumper); - if (const auto virtualIdOpt = VirtualDisplayId::tryCast(displayId)) { - std::lock_guard lock(mVirtualDisplaysMutex); - const auto virtualSnapshotIt = mVirtualDisplays.find(virtualIdOpt.value()); - if (virtualSnapshotIt != mVirtualDisplays.end()) { - virtualSnapshotIt->second.dump(dumper); - } + std::lock_guard lock(mVirtualDisplaysMutex); + if (const auto snapshotOpt = mVirtualDisplays.get(virtualId)) { + snapshotOpt->get().dump(dumper); } } } @@ -6022,10 +6117,11 @@ void SurfaceFlinger::dumpDisplays(std::string& result) const { void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { for (const auto& [token, display] : mDisplays) { - const auto displayId = PhysicalDisplayId::tryCast(display->getId()); + const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant()); if (!displayId) { continue; } + const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId); if (!hwcDisplayId) { continue; @@ -6034,6 +6130,7 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { StringAppendF(&result, "Display %s (HWC display %" PRIu64 "): ", to_string(*displayId).c_str(), *hwcDisplayId); + uint8_t port; DisplayIdentificationData data; if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) { @@ -6061,6 +6158,19 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { result.append(edid->displayName.data(), edid->displayName.length()); result.append("\"\n"); } + + for (const auto& [token, display] : mDisplays) { + const auto virtualDisplayId = asVirtualDisplayId(display->getDisplayIdVariant()); + if (virtualDisplayId) { + StringAppendF(&result, "Display %s (Virtual display): displayName=\"%s\"", + to_string(*virtualDisplayId).c_str(), display->getDisplayName().c_str()); + std::lock_guard lock(mVirtualDisplaysMutex); + if (const auto snapshotOpt = mVirtualDisplays.get(*virtualDisplayId)) { + StringAppendF(&result, " uniqueId=\"%s\"", snapshotOpt->get().uniqueId().c_str()); + } + result.append("\n"); + } + } } void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args, @@ -6231,7 +6341,7 @@ perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t t void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const { for (const auto& [token, display] : mDisplays) { - const auto displayId = HalDisplayId::tryCast(display->getId()); + const auto displayId = asHalDisplayId(display->getDisplayIdVariant()); if (!displayId) { continue; } @@ -7198,7 +7308,7 @@ static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) { return PERMISSION_DENIED; } -status_t SurfaceFlinger::setSchedFifo(bool enabled) { +void SurfaceFlinger::setSchedFifo(bool enabled, const char* whence) { static constexpr int kFifoPriority = 2; static constexpr int kOtherPriority = 0; @@ -7213,19 +7323,19 @@ status_t SurfaceFlinger::setSchedFifo(bool enabled) { } if (sched_setscheduler(0, sched_policy, ¶m) != 0) { - return -errno; + const char* kPolicy[] = {"SCHED_OTHER", "SCHED_FIFO"}; + ALOGW("%s: Failed to set %s: %s", whence, kPolicy[sched_policy == SCHED_FIFO], + strerror(errno)); } - - return NO_ERROR; } -status_t SurfaceFlinger::setSchedAttr(bool enabled) { +void SurfaceFlinger::setSchedAttr(bool enabled, const char* whence) { static const unsigned int kUclampMin = base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min"s, 0U); if (!kUclampMin) { // uclamp.min set to 0 (default), skip setting - return NO_ERROR; + return; } sched_attr attr = {}; @@ -7236,10 +7346,9 @@ status_t SurfaceFlinger::setSchedAttr(bool enabled) { attr.sched_util_max = 1024; if (syscall(__NR_sched_setattr, 0, &attr, 0)) { - return -errno; + const char* kAction[] = {"disable", "enable"}; + ALOGW("%s: Failed to %s uclamp.min: %s", whence, kAction[enabled], strerror(errno)); } - - return NO_ERROR; } namespace { @@ -7299,7 +7408,7 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, } wp<const DisplayDevice> displayWeak; - DisplayId displayId; + ftl::Optional<DisplayIdVariant> displayIdVariantOpt; ui::LayerStack layerStack; ui::Size reqSize(args.width, args.height); std::unordered_set<uint32_t> excludeLayerIds; @@ -7315,7 +7424,7 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, return; } displayWeak = display; - displayId = display->getId(); + displayIdVariantOpt = display->getDisplayIdVariant(); layerStack = display->getLayerStack(); displayIsSecure = display->isSecure(); @@ -7343,7 +7452,7 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, ScreenshotArgs screenshotArgs; screenshotArgs.captureTypeVariant = displayWeak; - screenshotArgs.displayId = displayId; + screenshotArgs.displayIdVariant = displayIdVariantOpt; screenshotArgs.sourceCrop = gui::aidl_utils::fromARect(captureArgs.sourceCrop); if (screenshotArgs.sourceCrop.isEmpty()) { screenshotArgs.sourceCrop = layerStackSpaceRect; @@ -7362,6 +7471,7 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args const sp<IScreenCaptureListener>& captureListener) { ui::LayerStack layerStack; wp<const DisplayDevice> displayWeak; + ftl::Optional<DisplayIdVariant> displayIdVariantOpt; ui::Size size; Rect layerStackSpaceRect; bool displayIsSecure; @@ -7377,6 +7487,7 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args } displayWeak = display; + displayIdVariantOpt = display->getDisplayIdVariant(); layerStack = display->getLayerStack(); layerStackSpaceRect = display->getLayerStackSpaceRect(); size = display->getLayerStackSpaceRect().getSize(); @@ -7411,7 +7522,7 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args ScreenshotArgs screenshotArgs; screenshotArgs.captureTypeVariant = displayWeak; - screenshotArgs.displayId = displayId; + screenshotArgs.displayIdVariant = displayIdVariantOpt; screenshotArgs.sourceCrop = layerStackSpaceRect; screenshotArgs.reqSize = size; screenshotArgs.dataspace = static_cast<ui::Dataspace>(args.dataspace); @@ -7820,15 +7931,16 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( // Otherwise for seamless transitions it's important to match the current // display state as the buffer will be shown under these same conditions, and we // want to avoid any flickers. - if (captureResults.capturedHdrLayers && !enableLocalTonemapping && - args.sdrWhitePointNits > 1.0f && !args.seamlessTransition) { - // Restrict the amount of HDR "headroom" in the screenshot to avoid - // over-dimming the SDR portion. 2.0 chosen by experimentation - constexpr float kMaxScreenshotHeadroom = 2.0f; - // TODO: Aim to update displayBrightnessNits earlier in screenshot - // path so ScreenshotArgs can be passed as const - args.displayBrightnessNits = std::min(args.sdrWhitePointNits * kMaxScreenshotHeadroom, - args.displayBrightnessNits); + if (captureResults.capturedHdrLayers) { + if (!enableLocalTonemapping && args.sdrWhitePointNits > 1.0f && !args.seamlessTransition) { + // Restrict the amount of HDR "headroom" in the screenshot to avoid + // over-dimming the SDR portion. 2.0 chosen by experimentation + constexpr float kMaxScreenshotHeadroom = 2.0f; + // TODO: Aim to update displayBrightnessNits earlier in screenshot + // path so ScreenshotArgs can be passed as const + args.displayBrightnessNits = std::min(args.sdrWhitePointNits * kMaxScreenshotHeadroom, + args.displayBrightnessNits); + } } else { args.displayBrightnessNits = args.sdrWhitePointNits; } @@ -7902,7 +8014,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( .layerStack = layerStack, .sourceCrop = args.sourceCrop, .buffer = std::move(buffer), - .displayId = args.displayId, + .displayIdVariant = args.displayIdVariant, .reqBufferSize = args.reqSize, .sdrWhitePointNits = args.sdrWhitePointNits, .displayBrightnessNits = args.displayBrightnessNits, @@ -8236,11 +8348,13 @@ int SurfaceFlinger::getGpuContextPriority() { int SurfaceFlinger::calculateMaxAcquiredBufferCount(Fps refreshRate, std::chrono::nanoseconds presentLatency) { - auto pipelineDepth = presentLatency.count() / refreshRate.getPeriodNsecs(); + int64_t pipelineDepth = presentLatency.count() / refreshRate.getPeriodNsecs(); if (presentLatency.count() % refreshRate.getPeriodNsecs()) { pipelineDepth++; } - return std::max(minAcquiredBuffers, static_cast<int64_t>(pipelineDepth - 1)); + const int64_t maxAcquiredBuffers = + std::min(pipelineDepth - 1, maxAcquiredBuffersOpt.value_or(pipelineDepth - 1)); + return std::max(minAcquiredBuffers, maxAcquiredBuffers); } status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const { @@ -8300,8 +8414,8 @@ sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const { // TODO(b/255635821): Choose the pacesetter display, considering both internal and external // displays. For now, pick the other internal display, assuming a dual-display foldable. return findDisplay([this](const DisplayDevice& display) REQUIRES(mStateLock) { - const auto idOpt = PhysicalDisplayId::tryCast(display.getId()); - return idOpt && *idOpt != mActiveDisplayId && display.isPoweredOn() && + const auto idOpt = asPhysicalDisplayId(display.getDisplayIdVariant()); + return idOpt.has_value() && *idOpt != mActiveDisplayId && display.isPoweredOn() && mPhysicalDisplays.get(*idOpt) .transform(&PhysicalDisplay::isInternal) .value_or(false); @@ -8358,10 +8472,6 @@ status_t SurfaceFlinger::getStalledTransactionInfo( 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); @@ -8745,16 +8855,16 @@ binder::Status SurfaceComposerAIDL::createConnection(sp<gui::ISurfaceComposerCli } } -binder::Status SurfaceComposerAIDL::createVirtualDisplay(const std::string& displayName, - bool isSecure, const std::string& uniqueId, - float requestedRefreshRate, - sp<IBinder>* outDisplay) { +binder::Status SurfaceComposerAIDL::createVirtualDisplay( + const std::string& displayName, bool isSecure, + gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId, + float requestedRefreshRate, sp<IBinder>* outDisplay) { status_t status = checkAccessPermission(); if (status != OK) { return binderStatusFromStatusT(status); } - *outDisplay = - mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId, requestedRefreshRate); + *outDisplay = mFlinger->createVirtualDisplay(displayName, isSecure, optimizationPolicy, + uniqueId, requestedRefreshRate); return binder::Status::ok(); } @@ -8842,6 +8952,7 @@ binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId, if (status == NO_ERROR) { // convert ui::StaticDisplayInfo to gui::StaticDisplayInfo outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType); + outInfo->port = info.port; outInfo->density = info.density; outInfo->secure = info.secure; outInfo->installOrientation = static_cast<gui::Rotation>(info.installOrientation); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 3f454ba1b8..c472c4c6d4 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -160,6 +160,7 @@ class DisplaySurface; class OutputLayer; struct CompositionRefreshArgs; +class DisplayCreationArgsBuilder; } // namespace compositionengine namespace renderengine { @@ -211,11 +212,9 @@ public: SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API; explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API; - // set main thread scheduling policy - static status_t setSchedFifo(bool enabled) ANDROID_API; - - // set main thread scheduling attributes - static status_t setSchedAttr(bool enabled); + // Set scheduling policy and attributes of main thread. + static void setSchedFifo(bool enabled, const char* whence); + static void setSchedAttr(bool enabled, const char* whence); static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; } @@ -240,6 +239,11 @@ public: // ISurfaceComposer.getMaxAcquiredBufferCount(). static int64_t minAcquiredBuffers; + // Controls the maximum acquired buffers SurfaceFlinger will suggest via + // ISurfaceComposer.getMaxAcquiredBufferCount(). + // Value is set through ro.surface_flinger.max_acquired_buffers. + static std::optional<int64_t> maxAcquiredBuffersOpt; + // Controls the maximum width and height in pixels that the graphics pipeline can support for // GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs. static uint32_t maxGraphicsWidth; @@ -531,6 +535,7 @@ private: // ISurfaceComposer implementation: sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure, + gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId, float requestedRefreshRate = 0.0f); status_t destroyVirtualDisplay(const sp<IBinder>& displayToken); @@ -729,8 +734,19 @@ private: void applyActiveMode(PhysicalDisplayId) REQUIRES(kMainThreadContext); // Called on the main thread in response to setPowerMode() - void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) + void setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode) REQUIRES(mStateLock, kMainThreadContext); + void setVirtualDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode) + REQUIRES(mStateLock, kMainThreadContext); + + // Adjusts thread scheduling according to the optimization policy + static void optimizeThreadScheduling( + const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy); + + // Enables or disables power optimizations depending on whether there are displays that should + // be optimized for performance. + void applyOptimizationPolicy(const char* whence) REQUIRES(kMainThreadContext) + REQUIRES(mStateLock); // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that // display. Falls back to the display's defaultModeId otherwise. @@ -871,7 +887,7 @@ private: std::variant<int32_t, wp<const DisplayDevice>> captureTypeVariant; // Display ID of the display the result will be on - std::optional<DisplayId> displayId{std::nullopt}; + ftl::Optional<DisplayIdVariant> displayIdVariant{std::nullopt}; // If true, transform is inverted from the parent layer snapshot bool childrenOnly{false}; @@ -1025,10 +1041,10 @@ private: // region of all screens presenting this layer stack. void invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty); - ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack) + ui::LayerFilter makeLayerFilterForDisplay(DisplayIdVariant displayId, ui::LayerStack layerStack) REQUIRES(mStateLock) { return {layerStack, - PhysicalDisplayId::tryCast(displayId) + asPhysicalDisplayId(displayId) .and_then(display::getPhysicalDisplay(mPhysicalDisplays)) .transform(&display::PhysicalDisplay::isInternal) .value_or(false)}; @@ -1067,7 +1083,8 @@ private: // Returns the active mode ID, or nullopt on hotplug failure. std::optional<DisplayModeId> processHotplugConnect(PhysicalDisplayId, hal::HWDisplayId, DisplayIdentificationInfo&&, - const char* displayString) + const char* displayString, + HWComposer::HotplugEvent event) REQUIRES(mStateLock, kMainThreadContext); void processHotplugDisconnect(PhysicalDisplayId, const char* displayString) REQUIRES(mStateLock, kMainThreadContext); @@ -1120,8 +1137,10 @@ private: void enableHalVirtualDisplays(bool); // Virtual display lifecycle for ID generation and HAL allocation. - VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, const std::string& uniqueId) - REQUIRES(mStateLock); + std::optional<VirtualDisplayIdVariant> acquireVirtualDisplay( + ui::Size, ui::PixelFormat, const std::string& uniqueId, + compositionengine::DisplayCreationArgsBuilder&) REQUIRES(mStateLock); + template <typename ID> void acquireVirtualDisplaySnapshot(ID displayId, const std::string& uniqueId) { std::lock_guard lock(mVirtualDisplaysMutex); @@ -1132,7 +1151,7 @@ private: } } - void releaseVirtualDisplay(VirtualDisplayId); + void releaseVirtualDisplay(VirtualDisplayIdVariant displayId); void releaseVirtualDisplaySnapshot(VirtualDisplayId displayId); // Returns a display other than `mActiveDisplayId` that can be activated, if any. @@ -1217,7 +1236,7 @@ private: bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const; - ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const + ui::Rotation getPhysicalDisplayOrientation(PhysicalDisplayId, bool isPrimary) const REQUIRES(mStateLock); void traverseLegacyLayers(const LayerVector::Visitor& visitor) const REQUIRES(kMainThreadContext); @@ -1440,8 +1459,6 @@ private: // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; - // Tracks the number of maximum queued buffers by layer owner Uid. - using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>; BufferCountTracker mBufferCountTracker; std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners @@ -1564,9 +1581,11 @@ public: const sp<IBinder>& layerHandle, sp<gui::IDisplayEventConnection>* outConnection) override; binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override; - binder::Status createVirtualDisplay(const std::string& displayName, bool isSecure, - const std::string& uniqueId, float requestedRefreshRate, - sp<IBinder>* outDisplay) override; + binder::Status createVirtualDisplay( + const std::string& displayName, bool isSecure, + gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, + const std::string& uniqueId, float requestedRefreshRate, + sp<IBinder>* outDisplay) override; binder::Status destroyVirtualDisplay(const sp<IBinder>& displayToken) override; binder::Status getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) override; binder::Status getPhysicalDisplayToken(int64_t displayId, sp<IBinder>* outDisplay) override; diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index 2676ca6777..3297c16113 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -139,7 +139,8 @@ perfetto::protos::LayerState TransactionProtoParser::toProto( colorProto->set_b(layer.color.b); } if (layer.what & layer_state_t::eTransparentRegionChanged) { - LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region()); + LayerProtoHelper::writeToProto(layer.getTransparentRegion(), + proto.mutable_transparent_region()); } if (layer.what & layer_state_t::eBufferTransformChanged) { proto.set_transform(layer.bufferTransform); @@ -191,33 +192,30 @@ perfetto::protos::LayerState TransactionProtoParser::toProto( } if (layer.what & layer_state_t::eInputInfoChanged) { - if (layer.windowInfoHandle) { - const gui::WindowInfo* inputInfo = layer.windowInfoHandle->getInfo(); - perfetto::protos::LayerState_WindowInfo* windowInfoProto = - proto.mutable_window_info_handle(); - windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get()); - windowInfoProto->set_layout_params_type( - static_cast<int32_t>(inputInfo->layoutParamsType)); - windowInfoProto->set_input_config(inputInfo->inputConfig.get()); - LayerProtoHelper::writeToProto(inputInfo->touchableRegion, - windowInfoProto->mutable_touchable_region()); - windowInfoProto->set_surface_inset(inputInfo->surfaceInset); - windowInfoProto->set_focusable( - !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)); - windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test( - gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)); - windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor); - perfetto::protos::Transform* transformProto = windowInfoProto->mutable_transform(); - transformProto->set_dsdx(inputInfo->transform.dsdx()); - transformProto->set_dtdx(inputInfo->transform.dtdx()); - transformProto->set_dtdy(inputInfo->transform.dtdy()); - transformProto->set_dsdy(inputInfo->transform.dsdy()); - transformProto->set_tx(inputInfo->transform.tx()); - transformProto->set_ty(inputInfo->transform.ty()); - windowInfoProto->set_replace_touchable_region_with_crop( - inputInfo->replaceTouchableRegionWithCrop); - windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId); - } + const gui::WindowInfo* inputInfo = &layer.getWindowInfo(); + perfetto::protos::LayerState_WindowInfo* windowInfoProto = + proto.mutable_window_info_handle(); + windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get()); + windowInfoProto->set_layout_params_type(static_cast<int32_t>(inputInfo->layoutParamsType)); + windowInfoProto->set_input_config(inputInfo->inputConfig.get()); + LayerProtoHelper::writeToProto(inputInfo->touchableRegion, + windowInfoProto->mutable_touchable_region()); + windowInfoProto->set_surface_inset(inputInfo->surfaceInset); + windowInfoProto->set_focusable( + !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)); + windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)); + windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor); + perfetto::protos::Transform* transformProto = windowInfoProto->mutable_transform(); + transformProto->set_dsdx(inputInfo->transform.dsdx()); + transformProto->set_dtdx(inputInfo->transform.dtdx()); + transformProto->set_dtdy(inputInfo->transform.dtdy()); + transformProto->set_dsdy(inputInfo->transform.dsdy()); + transformProto->set_tx(inputInfo->transform.tx()); + transformProto->set_ty(inputInfo->transform.ty()); + windowInfoProto->set_replace_touchable_region_with_crop( + inputInfo->replaceTouchableRegionWithCrop); + windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId); } if (layer.what & layer_state_t::eBackgroundColorChanged) { proto.set_bg_color_alpha(layer.bgColor.a); @@ -410,7 +408,9 @@ void TransactionProtoParser::fromProto(const perfetto::protos::LayerState& proto layer.color.b = colorProto.b(); } if (proto.what() & layer_state_t::eTransparentRegionChanged) { - LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion); + Region transparentRegion; + LayerProtoHelper::readFromProto(proto.transparent_region(), transparentRegion); + layer.updateTransparentRegion(transparentRegion); } if (proto.what() & layer_state_t::eBufferTransformChanged) { layer.bufferTransform = proto.transform(); @@ -486,7 +486,7 @@ void TransactionProtoParser::fromProto(const perfetto::protos::LayerState& proto windowInfoProto.replace_touchable_region_with_crop(); resolvedComposerState.touchCropId = windowInfoProto.crop_layer_id(); - layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo); + *layer.editWindowInfo() = inputInfo; } if (proto.what() & layer_state_t::eBackgroundColorChanged) { layer.bgColor.a = proto.bg_color_alpha(); diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp index 5cf42449c0..84d837c37a 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -102,11 +102,10 @@ bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& QueuedTransactionState transaction = parser.fromProto(entry.transactions(j)); for (auto& resolvedComposerState : transaction.states) { if (resolvedComposerState.state.what & layer_state_t::eInputInfoChanged) { - if (!resolvedComposerState.state.windowInfoHandle->getInfo()->inputConfig.test( + if (!resolvedComposerState.state.getWindowInfo().inputConfig.test( gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { // create a fake token since the FE expects a valid token - resolvedComposerState.state.windowInfoHandle->editInfo()->token = - sp<BBinder>::make(); + resolvedComposerState.state.editWindowInfo()->token = sp<BBinder>::make(); } } } diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index b22ec66819..2e8c8c1111 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -18,7 +18,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" -//#define LOG_NDEBUG 0 +// #define LOG_NDEBUG 0 #undef LOG_TAG #define LOG_TAG "TransactionCallbackInvoker" #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -28,6 +28,7 @@ #include "Utils/FenceUtils.h" #include <binder/IInterface.h> +#include <common/FlagManager.h> #include <common/trace.h> #include <utils/RefBase.h> @@ -142,8 +143,17 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle->transformHint, handle->currentMaxAcquiredBufferCount, eventStats, handle->previousReleaseCallbackId); + if (handle->bufferReleaseChannel && handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) { + if (FlagManager::getInstance().monitor_buffer_fences()) { + if (auto previousBuffer = handle->previousBuffer.lock()) { + previousBuffer->getBuffer() + ->getDependencyMonitor() + .addEgress(FenceTime::makeValid(handle->previousReleaseFence), + "Txn release"); + } + } mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel, handle->previousReleaseCallbackId, handle->previousReleaseFence, diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index 178ddbbe79..34f6ffc5da 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -25,6 +25,7 @@ #include <ftl/future.h> #include <gui/BufferReleaseChannel.h> #include <gui/ITransactionCompletedListener.h> +#include <renderengine/ExternalTexture.h> #include <ui/Fence.h> #include <ui/FenceResult.h> @@ -55,6 +56,7 @@ public: uint64_t previousFrameNumber = 0; ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel; + std::weak_ptr<renderengine::ExternalTexture> previousBuffer; }; class TransactionCallbackInvoker { diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp index 8786f6e592..13f65770cd 100644 --- a/services/surfaceflinger/common/Android.bp +++ b/services/surfaceflinger/common/Android.bp @@ -16,9 +16,9 @@ cc_defaults { ], shared_libs: [ "libSurfaceFlingerProp", - "server_configurable_flags", "libaconfig_storage_read_api_cc", "libtracing_perfetto", + "server_configurable_flags", ], static_libs: [ "librenderengine_includes", @@ -37,11 +37,12 @@ cc_library_static { "libsurfaceflinger_common_defaults", ], static_libs: [ - "libsurfaceflingerflags", "aconfig_hardware_flags_c_lib", + "android.companion.virtualdevice.flags-aconfig-cc", "android.os.flags-aconfig-cc", "android.server.display.flags-aconfig-cc", "libguiflags_no_apex", + "libsurfaceflingerflags", ], } @@ -51,44 +52,47 @@ cc_library_static { "libsurfaceflinger_common_defaults", ], static_libs: [ - "libsurfaceflingerflags_test", "aconfig_hardware_flags_c_lib", + "android.companion.virtualdevice.flags-aconfig-cc", "android.os.flags-aconfig-cc-test", "android.server.display.flags-aconfig-cc", "libguiflags_no_apex", + "libsurfaceflingerflags_test", ], } cc_defaults { name: "libsurfaceflinger_common_deps", shared_libs: [ - "server_configurable_flags", "libaconfig_storage_read_api_cc", "libtracing_perfetto", + "server_configurable_flags", ], static_libs: [ - "libsurfaceflinger_common", - "libsurfaceflingerflags", "aconfig_hardware_flags_c_lib", + "android.companion.virtualdevice.flags-aconfig-cc", "android.os.flags-aconfig-cc", "android.server.display.flags-aconfig-cc", "libguiflags_no_apex", + "libsurfaceflinger_common", + "libsurfaceflingerflags", ], } cc_defaults { name: "libsurfaceflinger_common_test_deps", shared_libs: [ - "server_configurable_flags", "libaconfig_storage_read_api_cc", "libtracing_perfetto", + "server_configurable_flags", ], static_libs: [ - "libsurfaceflinger_common_test", - "libsurfaceflingerflags_test", "aconfig_hardware_flags_c_lib", + "android.companion.virtualdevice.flags-aconfig-cc", "android.os.flags-aconfig-cc-test", "android.server.display.flags-aconfig-cc", "libguiflags_no_apex", + "libsurfaceflinger_common_test", + "libsurfaceflingerflags_test", ], } diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index f9aba9fe10..bf1035149b 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -26,8 +26,9 @@ #include <server_configurable_flags/get_flags.h> #include <cinttypes> -#include <android_os.h> +#include <android_companion_virtualdevice_flags.h> #include <android_hardware_flags.h> +#include <android_os.h> #include <com_android_graphics_libgui_flags.h> #include <com_android_graphics_surfaceflinger_flags.h> #include <com_android_server_display_feature_flags.h> @@ -125,9 +126,12 @@ void FlagManager::dump(std::string& result) const { DUMP_ACONFIG_FLAG(adpf_gpu_sf); DUMP_ACONFIG_FLAG(adpf_native_session_manager); DUMP_ACONFIG_FLAG(adpf_use_fmq_channel); + DUMP_ACONFIG_FLAG(correct_virtual_display_power_state); DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout); DUMP_ACONFIG_FLAG(increase_missed_frame_jank_threshold); + DUMP_ACONFIG_FLAG(monitor_buffer_fences); DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display); + DUMP_ACONFIG_FLAG(vsync_predictor_recovery); /// Trunk stable readonly flags /// /// IMPORTANT - please keep alphabetize to reduce merge conflicts @@ -138,7 +142,6 @@ void FlagManager::dump(std::string& result) const { DUMP_ACONFIG_FLAG(begone_bright_hlg); DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved); DUMP_ACONFIG_FLAG(commit_not_composited); - DUMP_ACONFIG_FLAG(connected_display); DUMP_ACONFIG_FLAG(connected_display_hdr); DUMP_ACONFIG_FLAG(correct_dpi_with_display_size); DUMP_ACONFIG_FLAG(deprecate_frame_tracker); @@ -162,7 +165,6 @@ void FlagManager::dump(std::string& result) const { DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed); DUMP_ACONFIG_FLAG(local_tonemap_screenshots); DUMP_ACONFIG_FLAG(misc1); - DUMP_ACONFIG_FLAG(multithreaded_present); DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off); DUMP_ACONFIG_FLAG(override_trusted_overlay); DUMP_ACONFIG_FLAG(protected_if_client); @@ -251,14 +253,12 @@ FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED, /// Trunk stable readonly flags /// FLAG_MANAGER_ACONFIG_FLAG(adpf_fmq_sf, "") FLAG_MANAGER_ACONFIG_FLAG(arr_setframerate_gte_enum, "debug.sf.arr_setframerate_gte_enum") -FLAG_MANAGER_ACONFIG_FLAG(connected_display, "") FLAG_MANAGER_ACONFIG_FLAG(enable_small_area_detection, "") FLAG_MANAGER_ACONFIG_FLAG(stable_edid_ids, "debug.sf.stable_edid_ids") FLAG_MANAGER_ACONFIG_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr") FLAG_MANAGER_ACONFIG_FLAG(misc1, "") FLAG_MANAGER_ACONFIG_FLAG(vrr_config, "debug.sf.enable_vrr_config") FLAG_MANAGER_ACONFIG_FLAG(hdcp_level_hal, "") -FLAG_MANAGER_ACONFIG_FLAG(multithreaded_present, "debug.sf.multithreaded_present") FLAG_MANAGER_ACONFIG_FLAG(add_sf_skipped_frames_to_trace, "") FLAG_MANAGER_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency, "") FLAG_MANAGER_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved, @@ -304,9 +304,13 @@ FLAG_MANAGER_ACONFIG_FLAG(adpf_gpu_sf, "") FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, ""); FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, ""); FLAG_MANAGER_ACONFIG_FLAG(increase_missed_frame_jank_threshold, ""); +FLAG_MANAGER_ACONFIG_FLAG(monitor_buffer_fences, ""); +FLAG_MANAGER_ACONFIG_FLAG(vsync_predictor_recovery, ""); /// Trunk stable server (R/W) flags from outside SurfaceFlinger /// FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os) +FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(correct_virtual_display_power_state, "", + android::companion::virtualdevice::flags) /// Trunk stable readonly flags from outside SurfaceFlinger /// FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "", diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index de3f359d7e..8f361ac610 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -60,9 +60,12 @@ public: bool adpf_native_session_manager() const; bool adpf_use_fmq_channel() const; bool adpf_use_fmq_channel_fixed() const; + bool correct_virtual_display_power_state() const; bool graphite_renderengine_preview_rollout() const; bool increase_missed_frame_jank_threshold() const; + bool monitor_buffer_fences() const; bool refresh_rate_overlay_on_external_display() const; + bool vsync_predictor_recovery() const; /// Trunk stable readonly flags /// /// IMPORTANT - please keep alphabetize to reduce merge conflicts @@ -73,7 +76,6 @@ public: bool begone_bright_hlg() const; bool cache_when_source_crop_layer_only_moved() const; bool commit_not_composited() const; - bool connected_display() const; bool connected_display_hdr() const; bool correct_dpi_with_display_size() const; bool deprecate_frame_tracker() const; @@ -98,7 +100,6 @@ public: bool local_tonemap_screenshots() const; bool luts_api() const; bool misc1() const; - bool multithreaded_present() const; bool no_vsyncs_on_screen_off() const; bool override_trusted_overlay() const; bool protected_if_client() const; diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index 73dfa9fa2f..4afcd00ecb 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -77,7 +77,7 @@ static void startDisplayService() { } } -int main(int, char**) { +int main() { signal(SIGPIPE, SIG_IGN); hardware::configureRpcThreadpool(1 /* maxThreads */, @@ -91,9 +91,7 @@ int main(int, char**) { // Set uclamp.min setting on all threads, maybe an overkill but we want // to cover important threads like RenderEngine. - if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) { - ALOGW("Failed to set uclamp.min during boot: %s", strerror(errno)); - } + SurfaceFlinger::setSchedAttr(true, __func__); // The binder threadpool we start will inherit sched policy and priority // of (this) creating thread. We want the binder thread pool to have @@ -160,14 +158,8 @@ int main(int, char**) { startDisplayService(); // dependency on SF getting registered above - if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) { - ALOGW("Failed to set SCHED_FIFO during boot: %s", strerror(errno)); - } - - // run surface flinger in this thread + SurfaceFlinger::setSchedFifo(true, __func__); flinger->run(); - - return 0; } // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index fa1da45e41..d412a19f3c 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -216,6 +216,13 @@ flag { } # local_tonemap_screenshots flag { + name: "monitor_buffer_fences" + namespace: "core_graphics" + description: "Monitors fences for each buffer" + bug: "360932099" +} # monitor_buffer_fences + +flag { name: "no_vsyncs_on_screen_off" namespace: "core_graphics" description: "Stop vsync / Choreographer callbacks to apps when the screen is off" @@ -321,6 +328,16 @@ flag { } # vrr_bugfix_dropped_frame flag { + name: "vsync_predictor_recovery" + namespace: "core_graphics" + description: "Recover the vsync predictor from bad vsync model" + bug: "385059265" + metadata { + purpose: PURPOSE_BUGFIX + } + } # vsync_predictor_recovery + +flag { name: "window_blur_kawase2" namespace: "core_graphics" description: "Flag for using Kawase2 algorithm for window blur" diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 0ad5ac9956..bfafb655d9 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -483,6 +483,16 @@ prop { prop_name: "ro.surface_flinger.min_acquired_buffers" } +# Defines the maximum acquired buffers SurfaceFlinger will suggest via +# ISurfaceComposer.getMaxAcquiredBufferCount(). +prop { + api_name: "max_acquired_buffers" + type: Long + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.max_acquired_buffers" +} + # When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for # buffers when they are evicted from the app cache by using additional setLayerBuffer commands. # Ideally, this behavior would always be enabled to reduce graphics memory consumption. However, diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index 00173009f5..e2ac233f6b 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -82,6 +82,11 @@ props { prop_name: "ro.surface_flinger.ignore_hdr_camera_layers" } prop { + api_name: "max_acquired_buffers" + type: Long + prop_name: "ro.surface_flinger.max_acquired_buffers" + } + prop { api_name: "max_frame_buffer_acquired_buffers" type: Long prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers" diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml index 000628f598..ad43cdcc5d 100644 --- a/services/surfaceflinger/tests/AndroidTest.xml +++ b/services/surfaceflinger/tests/AndroidTest.xml @@ -19,6 +19,9 @@ <option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <option name="screen-always-on" value="on" /> + </target_preparer> <option name="test-suite-tag" value="apct" /> <test class="com.android.tradefed.testtype.GTest" > <option name="native-test-device-path" value="/data/local/tmp" /> diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index c95c875746..c91f1ea760 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -48,7 +48,11 @@ public: ui::DisplayMode displayMode; SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode); - const ui::Size& resolution = displayMode.resolution; + ui::Size resolution = displayMode.resolution; + if (displayState.orientation == ui::Rotation::Rotation90 || + displayState.orientation == ui::Rotation::Rotation270) { + std::swap(resolution.width, resolution.height); + } sp<IBinder> vDisplay; @@ -93,8 +97,8 @@ public: #else t.setDisplaySurface(vDisplay, producer); #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) - t.setDisplayProjection(vDisplay, displayState.orientation, - Rect(displayState.layerStackSpaceRect), Rect(resolution)); + t.setDisplayProjection(vDisplay, ui::Rotation::Rotation0, Rect(resolution), + Rect(resolution)); t.setDisplayLayerStack(vDisplay, layerStack); t.setLayerStack(mirrorSc, layerStack); t.apply(); diff --git a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp index 0925118f87..fec6242728 100644 --- a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp +++ b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp @@ -90,5 +90,46 @@ static void updateClientStatesNoChanges(benchmark::State& state) { } BENCHMARK(updateClientStatesNoChanges); +static void propagateManyHiddenChildren(benchmark::State& state) { + LayerLifecycleManager lifecycleManager; + LayerLifecycleManagerHelper helper(lifecycleManager); + + helper.createRootLayer(0); + for (uint32_t i = 1; i < 50; ++i) { + helper.createLayer(i, i - 1); + } + + helper.hideLayer(0); + + LayerHierarchyBuilder hierarchyBuilder; + DisplayInfo info; + info.info.logicalHeight = 100; + info.info.logicalWidth = 100; + DisplayInfos displayInfos; + displayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info); + ShadowSettings globalShadowSettings; + + LayerSnapshotBuilder snapshotBuilder; + + int i = 1; + for (auto _ : state) { + i++; + helper.setAlpha(0, (1 + (i % 255)) / 255.0f); + + if (lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { + hierarchyBuilder.update(lifecycleManager); + } + LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(), + .layerLifecycleManager = lifecycleManager, + .displays = displayInfos, + .globalShadowSettings = globalShadowSettings, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + snapshotBuilder.update(args); + lifecycleManager.commitChanges(); + } +} +BENCHMARK(propagateManyHiddenChildren); + } // namespace } // namespace android::surfaceflinger diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h index 7910e775f6..82390ac6f3 100644 --- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -265,9 +265,8 @@ public: transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; transactions.back().states.front().layerId = id; - transactions.back().states.front().state.windowInfoHandle = - sp<gui::WindowInfoHandle>::make(); - auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + auto inputInfo = transactions.back().states.front().state.editWindowInfo(); + *inputInfo = {}; inputInfo->touchableRegion = region; inputInfo->token = sp<BBinder>::make(); mLifecycleManager.applyTransactions(transactions); @@ -280,9 +279,8 @@ public: transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; transactions.back().states.front().layerId = id; - transactions.back().states.front().state.windowInfoHandle = - sp<gui::WindowInfoHandle>::make(); - auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + auto inputInfo = transactions.back().states.front().state.editWindowInfo(); + *inputInfo = {}; if (!inputInfo->token) { inputInfo->token = sp<BBinder>::make(); } @@ -299,9 +297,8 @@ public: transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; transactions.back().states.front().layerId = id; - transactions.back().states.front().state.windowInfoHandle = - sp<gui::WindowInfoHandle>::make(); - auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + auto inputInfo = transactions.back().states.front().state.editWindowInfo(); + *inputInfo = {}; inputInfo->touchableRegion = region; inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop; transactions.back().states.front().touchCropId = touchCropId; @@ -455,9 +452,8 @@ public: transactions.emplace_back(); transactions.back().states.push_back({}); - transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged; transactions.back().states.front().layerId = id; - transactions.back().states.front().state.surfaceDamageRegion = damageRegion; + transactions.back().states.front().state.updateSurfaceDamageRegion(damageRegion); mLifecycleManager.applyTransactions(transactions); } diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 9ece312850..c342e1eebb 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -468,7 +468,7 @@ struct BaseLayerProperties { layer.externalTexture = buffer; layer.bufferData->acquireFence = Fence::NO_FENCE; layer.dataspace = ui::Dataspace::UNKNOWN; - layer.surfaceDamageRegion = Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)); + layer.setSurfaceDamageRegion(Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH))); Mock::VerifyAndClear(test->mRenderEngine); } diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 7f9296f3ae..75182e5fbb 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -114,7 +114,11 @@ public: // Test instances TestableSurfaceFlinger mFlinger; + sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); + sp<compositionengine::mock::DisplaySurface> mDisplaySurface = + sp<compositionengine::mock::DisplaySurface>::make(); + sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN); @@ -179,7 +183,7 @@ struct DisplayIdGetter; template <typename PhysicalDisplay> struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> { - static PhysicalDisplayId get() { + static DisplayIdVariant get() { if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) { return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY) ? LEGACY_DISPLAY_TYPE_PRIMARY @@ -195,12 +199,12 @@ struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> { template <VirtualDisplayId::BaseId displayId> struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> { - static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); } + static DisplayIdVariant get() { return HalVirtualDisplayId(displayId); } }; template <> struct DisplayIdGetter<GpuVirtualDisplayIdType> { - static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); } + static DisplayIdVariant get() { return GpuVirtualDisplayId(0); } }; template <typename> @@ -294,6 +298,7 @@ struct DisplayVariant { injector.setSecure(static_cast<bool>(SECURE)); injector.setNativeWindow(test->mNativeWindow); + injector.setDisplaySurface(test->mDisplaySurface); // Creating a DisplayDevice requires getting default dimensions from the // native window along with some other initial setup. @@ -369,9 +374,8 @@ struct HwcDisplayVariant { // Called by tests to inject a HWC display setup template <hal::PowerMode kPowerMode = hal::PowerMode::ON> static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) { - const auto displayId = DisplayVariant::DISPLAY_ID::get(); - ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId)); - TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE, + TestableSurfaceFlinger::FakeHwcDisplayInjector(DisplayVariant::DISPLAY_ID::get(), + HWC_DISPLAY_TYPE, static_cast<bool>(DisplayVariant::PRIMARY)) .setHwcDisplayId(HWC_DISPLAY_ID) .setResolution(DisplayVariant::RESOLUTION) @@ -658,9 +662,8 @@ struct HwcVirtualDisplayVariant const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); - const auto displayId = Base::DISPLAY_ID::get(); auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() - .setId(displayId) + .setId(Base::DISPLAY_ID::get()) .setPixels(Base::RESOLUTION) .setIsSecure(static_cast<bool>(Base::SECURE)) .setPowerAdvisor(&test->mPowerAdvisor) @@ -673,7 +676,12 @@ struct HwcVirtualDisplayVariant ceDisplayArgs); // Insert display data so that the HWC thinks it created the virtual display. - test->mFlinger.mutableHwcDisplayData().try_emplace(displayId); + const auto ceDisplayIdVar = compositionDisplay->getDisplayIdVariant(); + LOG_ALWAYS_FATAL_IF(!ceDisplayIdVar); + EXPECT_EQ(*ceDisplayIdVar, Base::DISPLAY_ID::get()); + const auto displayId = asHalDisplayId(*ceDisplayIdVar); + LOG_ALWAYS_FATAL_IF(!displayId); + test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId); return compositionDisplay; } @@ -811,8 +819,9 @@ using HwcVirtualDisplayCase = inline DisplayModePtr createDisplayMode(DisplayModeId modeId, Fps refreshRate, int32_t group = 0, ui::Size resolution = ui::Size(1920, 1080)) { - return mock::createDisplayMode(modeId, refreshRate, group, resolution, - PrimaryDisplayVariant::DISPLAY_ID::get()); + const auto physicalDisplayId = asPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get()); + LOG_ALWAYS_FATAL_IF(!physicalDisplayId); + return mock::createDisplayMode(modeId, refreshRate, group, resolution, *physicalDisplayId); } } // namespace android diff --git a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h index 90e716ff1f..edcb639f82 100644 --- a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h +++ b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h @@ -48,8 +48,10 @@ struct DualDisplayTransactionTest : DisplayTransactionTest { } } - static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get(); - static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get(); + static inline PhysicalDisplayId kInnerDisplayId = + asPhysicalDisplayId(InnerDisplayVariant::DISPLAY_ID::get()).value(); + static inline PhysicalDisplayId kOuterDisplayId = + asPhysicalDisplayId(OuterDisplayVariant::DISPLAY_ID::get()).value(); sp<DisplayDevice> mInnerDisplay, mOuterDisplay; }; diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 6f15db8beb..76e01a6e7d 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -23,7 +23,6 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <gui/DisplayEventReceiver.h> #include <log/log.h> #include <scheduler/VsyncConfig.h> #include <utils/Errors.h> @@ -112,8 +111,6 @@ protected: void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime); void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride>); - void expectQueuedBufferCountReceivedByConnection( - ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount); void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime, nsecs_t deadlineTimestamp) { @@ -147,7 +144,6 @@ protected: sp<MockEventThreadConnection> mConnection; sp<MockEventThreadConnection> mThrottledConnection; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; - std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders; std::chrono::nanoseconds mVsyncPeriod; @@ -380,14 +376,6 @@ void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection( EXPECT_EQ(expectedDisplayId, event.header.displayId); } -void EventThreadTest::expectQueuedBufferCountReceivedByConnection( - ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) { - auto args = connectionEventRecorder.waitForCall(); - ASSERT_TRUE(args.has_value()); - const auto& event = std::get<0>(args.value()); - EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers); -} - namespace { using namespace testing; @@ -880,63 +868,6 @@ TEST_F(EventThreadTest, postHcpLevelsChanged) { EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel); } -TEST_F(EventThreadTest, connectionReceivesBufferStuffing) { - setupEventThread(); - - // Create a connection that will experience buffer stuffing. - ConnectionEventRecorder stuffedConnectionEventRecorder{0}; - sp<MockEventThreadConnection> stuffedConnection = - createConnection(stuffedConnectionEventRecorder, - gui::ISurfaceComposer::EventRegistration::modeChanged | - gui::ISurfaceComposer::EventRegistration::frameRateOverride, - 111); - - // Add a connection and buffer count to the list of stuffed Uids that will receive - // data in the next vsync event. - BufferStuffingMap bufferStuffedUids; - bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3); - mThread->addBufferStuffedUids(bufferStuffedUids); - mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder); - - // Signal that we want the next vsync event to be posted to two connections. - mThread->requestNextVsync(mConnection); - mThread->requestNextVsync(stuffedConnection); - onVSyncEvent(123, 456, 789); - - // Vsync event data contains number of queued buffers. - expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0); - expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3); -} - -TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) { - setupEventThread(); - - // Create a connection with the same Uid as another connection. - ConnectionEventRecorder secondConnectionEventRecorder{0}; - sp<MockEventThreadConnection> secondConnection = - createConnection(secondConnectionEventRecorder, - gui::ISurfaceComposer::EventRegistration::modeChanged | - gui::ISurfaceComposer::EventRegistration::frameRateOverride, - mConnectionUid); - - // Add connection Uid and buffer count to the list of stuffed Uids that will receive - // data in the next vsync event. - BufferStuffingMap bufferStuffedUids; - bufferStuffedUids.try_emplace(mConnectionUid, 3); - mThread->addBufferStuffedUids(bufferStuffedUids); - mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder); - mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder); - - // Signal that we want the next vsync event to be posted to two connections. - mThread->requestNextVsync(mConnection); - mThread->requestNextVsync(secondConnection); - onVSyncEvent(123, 456, 789); - - // Vsync event data contains number of queued buffers. - expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3); - expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3); -} - } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp index a5b347a43c..c6da1a1a79 100644 --- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp @@ -125,13 +125,13 @@ TEST_F(FlagManagerTest, DISABLED_returnsOverrideFalse) { TEST_F(FlagManagerTest, ignoresOverrideInUnitTestMode) { mFlagManager.setUnitTestMode(); - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + SET_FLAG_FOR_TEST(flags::no_vsyncs_on_screen_off, true); // If this has not been called in this process, it will be called. // Regardless, the result is ignored. EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(false)); - EXPECT_EQ(true, mFlagManager.multithreaded_present()); + EXPECT_EQ(true, mFlagManager.no_vsyncs_on_screen_off()); } TEST_F(FlagManagerTest, returnsValue) { diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 7aad84b545..07356b9ec9 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -261,6 +261,40 @@ TEST_F(LayerSnapshotTest, AlphaInheritedByChildren) { EXPECT_EQ(getSnapshot(1221)->alpha, 0.25f); } +TEST_F(LayerSnapshotTest, AlphaInheritedByChildWhenParentIsHiddenByInvalidTransform) { + setMatrix(1, 0, 0, 0, 0); + update(mSnapshotBuilder); + mLifecycleManager.commitChanges(); + + setAlpha(1, 0.5); + update(mSnapshotBuilder); + mLifecycleManager.commitChanges(); + + setMatrix(1, 1, 0, 0, 1); + update(mSnapshotBuilder); + mLifecycleManager.commitChanges(); + + EXPECT_EQ(getSnapshot(1)->alpha, 0.5f); + EXPECT_EQ(getSnapshot(11)->alpha, 0.5f); +} + +TEST_F(LayerSnapshotTest, AlphaInheritedByChildWhenParentIsHidden) { + hideLayer(1); + update(mSnapshotBuilder); + mLifecycleManager.commitChanges(); + + setAlpha(1, 0.5); + update(mSnapshotBuilder); + mLifecycleManager.commitChanges(); + + showLayer(1); + update(mSnapshotBuilder); + mLifecycleManager.commitChanges(); + + EXPECT_EQ(getSnapshot(1)->alpha, 0.5f); + EXPECT_EQ(getSnapshot(11)->alpha, 0.5f); +} + // Change states TEST_F(LayerSnapshotTest, UpdateClearsPreviousChangeStates) { setCrop(1, Rect(1, 2, 3, 4)); @@ -1642,8 +1676,8 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { 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(); + auto inputInfo = transactions.back().states.front().state.editWindowInfo(); + *inputInfo = {}; inputInfo->token = sp<BBinder>::make(); mLifecycleManager.applyTransactions(transactions); @@ -1671,8 +1705,8 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInputShouldNotBeIncluded) { 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(); + auto inputInfo = transactions.back().states.front().state.editWindowInfo(); + *inputInfo = {}; inputInfo->token = sp<BBinder>::make(); hideLayer(3); mLifecycleManager.applyTransactions(transactions); @@ -1879,9 +1913,6 @@ TEST_F(LayerSnapshotTest, hideLayerWithNanMatrix) { } TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) { - if (!com::android::graphics::libgui::flags::edge_extension_shader()) { - GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; - } setCrop(1, Rect(0, 0, 20, 20)); setBuffer(1221, std::make_shared<renderengine::mock::FakeExternalTexture>(20 /* width */, @@ -1920,9 +1951,6 @@ TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) { TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) { // The left bound is extended when shifting to the right - if (!com::android::graphics::libgui::flags::edge_extension_shader()) { - GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; - } setCrop(1, Rect(0, 0, 20, 20)); const int texSize = 10; setBuffer(1221, @@ -1942,9 +1970,6 @@ TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) { TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) { // The right bound is extended when shifting to the left - if (!com::android::graphics::libgui::flags::edge_extension_shader()) { - GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; - } const int crop = 20; setCrop(1, Rect(0, 0, crop, crop)); const int texSize = 10; @@ -1965,9 +1990,6 @@ TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) { TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) { // The top bound is extended when shifting to the bottom - if (!com::android::graphics::libgui::flags::edge_extension_shader()) { - GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; - } setCrop(1, Rect(0, 0, 20, 20)); const int texSize = 10; setBuffer(1221, @@ -1987,9 +2009,6 @@ TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) { TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) { // The bottom bound is extended when shifting to the top - if (!com::android::graphics::libgui::flags::edge_extension_shader()) { - GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; - } const int crop = 20; setCrop(1, Rect(0, 0, crop, crop)); const int texSize = 10; @@ -2010,9 +2029,6 @@ TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) { TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) { // The left bound is extended when shifting to the right - if (!com::android::graphics::libgui::flags::edge_extension_shader()) { - GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; - } const int crop = 20; setCrop(1, Rect(0, 0, crop, crop)); const int texSize = 10; diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 1fc874dad8..116fcd9f6d 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -143,7 +143,7 @@ TEST_F(SchedulerTest, registerDisplay) FTL_FAKE_GUARD(kMainThreadContext) { kDisplay1Mode60->getId())); // TODO(b/241285191): Restore once VsyncSchedule::getPendingHardwareVsyncState is called by - // Scheduler::setDisplayPowerMode rather than SF::setPowerModeInternal. + // Scheduler::setDisplayPowerMode rather than SF::setPhysicalDisplayPowerMode. #if 0 // Hardware VSYNC should be disabled for newly registered displays. EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId2, false)).Times(1); @@ -254,18 +254,43 @@ TEST_F(SchedulerTest, emitModeChangeEvent) { } TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { - EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms)); - EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms)); - EXPECT_EQ(3, mFlinger.calculateMaxAcquiredBufferCount(120_Hz, 30ms)); + struct TestCase { + Fps refreshRate; + std::chrono::nanoseconds presentLatency; + int expectedBufferCount; + }; - EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms)); + const auto verifyTestCases = [&](std::vector<TestCase> tests) { + for (const auto testCase : tests) { + EXPECT_EQ(testCase.expectedBufferCount, + mFlinger.calculateMaxAcquiredBufferCount(testCase.refreshRate, + testCase.presentLatency)); + } + }; - EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms)); + std::vector<TestCase> testCases{{60_Hz, 30ms, 1}, + {90_Hz, 30ms, 2}, + {120_Hz, 30ms, 3}, + {60_Hz, 40ms, 2}, + {60_Hz, 10ms, 1}}; + verifyTestCases(testCases); const auto savedMinAcquiredBuffers = mFlinger.mutableMinAcquiredBuffers(); mFlinger.mutableMinAcquiredBuffers() = 2; - EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms)); + verifyTestCases({{60_Hz, 10ms, 2}}); mFlinger.mutableMinAcquiredBuffers() = savedMinAcquiredBuffers; + + const auto savedMaxAcquiredBuffers = mFlinger.mutableMaxAcquiredBuffers(); + mFlinger.mutableMaxAcquiredBuffers() = 2; + testCases = {{60_Hz, 30ms, 1}, + {90_Hz, 30ms, 2}, + {120_Hz, 30ms, 2}, // max buffers allowed is 2 + {60_Hz, 40ms, 2}, + {60_Hz, 10ms, 1}}; + verifyTestCases(testCases); + mFlinger.mutableMaxAcquiredBuffers() = 3; // max buffers allowed is 3 + verifyTestCases({{120_Hz, 30ms, 3}}); + mFlinger.mutableMaxAcquiredBuffers() = savedMaxAcquiredBuffers; } MATCHER(Is120Hz, "") { @@ -716,8 +741,6 @@ TEST_F(SchedulerTest, resyncAllDoNotAllow) FTL_FAKE_GUARD(kMainThreadContext) { } TEST_F(SchedulerTest, resyncAllSkipsOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); - // resyncAllToHardwareVsync will result in requesting hardware VSYNC on display 1, which is on, // but not on display 2, which is off. EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1); @@ -738,28 +761,6 @@ TEST_F(SchedulerTest, resyncAllSkipsOffDisplays) FTL_FAKE_GUARD(kMainThreadConte mScheduler->resyncAllToHardwareVsync(kAllowToEnable); } -TEST_F(SchedulerTest, resyncAllLegacyAppliesToOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, false); - - // In the legacy code, prior to the flag, resync applied to OFF displays. - EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1); - EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1); - - mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON); - - mScheduler->registerDisplay(kDisplayId2, - std::make_shared<RefreshRateSelector>(kDisplay2Modes, - kDisplay2Mode60->getId())); - ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2)); - - static constexpr bool kDisallow = true; - mScheduler->disableHardwareVsync(kDisplayId1, kDisallow); - mScheduler->disableHardwareVsync(kDisplayId2, kDisallow); - - static constexpr bool kAllowToEnable = true; - mScheduler->resyncAllToHardwareVsync(kAllowToEnable); -} - class AttachedChoreographerTest : public SchedulerTest { protected: void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps, diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp index aadff760fe..aa48c1b26a 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp @@ -50,9 +50,9 @@ public: EXPECT_EQ(display.requestedRefreshRate, Fps::fromValue(requestedRefreshRate)); EXPECT_EQ(name.c_str(), display.displayName); - const VirtualDisplayId vid = GpuVirtualDisplayId(baseId); sp<DisplayDevice> device = - mFlinger.createVirtualDisplayDevice(displayToken, vid, requestedRefreshRate); + mFlinger.createVirtualDisplayDevice(displayToken, GpuVirtualDisplayId(baseId), + requestedRefreshRate); EXPECT_TRUE(device->isVirtual()); device->adjustRefreshRate(Fps::fromValue(pacesetterDisplayRefreshRate)); @@ -141,7 +141,11 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForUniqueId) { // -------------------------------------------------------------------- // Invocation - sp<IBinder> displayToken = mFlinger.createVirtualDisplay(kDisplayName, false, kUniqueId); + sp<IBinder> displayToken = + mFlinger.createVirtualDisplay(kDisplayName, false, + gui::ISurfaceComposer::OptimizationPolicy:: + optimizeForPower, + kUniqueId); // -------------------------------------------------------------------- // Postconditions diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index a506873d4c..3f710fdf6b 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -171,16 +171,22 @@ protected: static constexpr DisplayModeId kModeId90{1}; static constexpr DisplayModeId kModeId120{2}; static constexpr DisplayModeId kModeId90_4K{3}; + static constexpr DisplayModeId kModeId60_8K{4}; static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz, 0); static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz, 1); static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz, 2); static constexpr ui::Size kResolution4K{3840, 2160}; + static constexpr ui::Size kResolution8K{7680, 4320}; + static inline const DisplayModePtr kMode90_4K = createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K); + static inline const DisplayModePtr kMode60_8K = + createDisplayMode(kModeId60_8K, 60_Hz, 4, kResolution8K); - static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K); + static inline const DisplayModes kModes = + makeModes(kMode60, kMode90, kMode120, kMode90_4K, kMode60_8K); }; void DisplayModeSwitchingTest::setupScheduler( @@ -326,6 +332,8 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { } TEST_F(DisplayModeSwitchingTest, changeResolutionWithoutRefreshRequired) { + SET_FLAG_FOR_TEST(flags::synced_resolution_switch, false); + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60)); EXPECT_EQ(NO_ERROR, @@ -360,9 +368,45 @@ TEST_F(DisplayModeSwitchingTest, changeResolutionWithoutRefreshRequired) { EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90_4K)); } -TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { - SET_FLAG_FOR_TEST(flags::connected_display, true); +TEST_F(DisplayModeSwitchingTest, changeResolutionSynced) { + SET_FLAG_FOR_TEST(flags::synced_resolution_switch, true); + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60)); + + // PrimaryDisplayVariant has a 4K size, so switch to 8K. + EXPECT_EQ(NO_ERROR, + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId60_8K, + 60_Hz))); + + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K)); + + // The mode should not be set until the commit that resizes the display. + mFlinger.commit(); + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K)); + mFlinger.commit(); + EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K)); + + // Set the display size to match the resolution. + DisplayState state; + state.what = DisplayState::eDisplaySizeChanged; + state.token = mDisplay->getDisplayToken().promote(); + state.width = static_cast<uint32_t>(kResolution8K.width); + state.height = static_cast<uint32_t>(kResolution8K.height); + + // The next commit should set the mode and resize the framebuffer. + const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; + EXPECT_CALL(*mDisplaySurface, resizeBuffers(kResolution8K)); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId60_8K); + + constexpr bool kModeset = true; + mFlinger.setDisplayStateLocked(state); + mFlinger.configureAndCommit(kModeset); + + EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60_8K)); +} + +TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -371,8 +415,8 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120)); - mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF); - mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON); EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120)); @@ -402,8 +446,8 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60)); - mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF); - mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON); EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60)); @@ -427,8 +471,6 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { } TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { - SET_FLAG_FOR_TEST(flags::connected_display, true); - const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -437,8 +479,8 @@ TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120)); - mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON); - mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON); EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120)); @@ -480,7 +522,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); // Power off the display before the mode has been set. - mFlinger.setPowerModeInternal(mDisplay, hal::PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(mDisplay, hal::PowerMode::OFF); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); @@ -497,8 +539,6 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { } TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { - SET_FLAG_FOR_TEST(flags::connected_display, true); - const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -507,8 +547,8 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120)); - mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON); - mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON); EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120)); @@ -525,7 +565,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); // Power off the outer display before the mode has been set. - mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::OFF); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); @@ -542,8 +582,8 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90)); EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60)); - mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF); - mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp index 1335640342..eac5a8e9c5 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp @@ -68,13 +68,9 @@ void DisplayTransactionCommitTest::setupCommonPreconditions() { template <typename Case, bool connected> void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) { - const auto convert = [](auto physicalDisplayId) { - return std::make_optional(DisplayId{physicalDisplayId}); - }; - - EXPECT_CALL(*eventThread, - onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected)) - .Times(1); + const auto physicalDisplayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(physicalDisplayId); + EXPECT_CALL(*eventThread, onHotplugReceived(*physicalDisplayId, connected)).Times(1); } template <typename Case> @@ -111,7 +107,7 @@ void DisplayTransactionCommitTest::verifyDisplayIsConnected(const sp<IBinder>& d std::optional<DisplayDeviceState::Physical> expectedPhysical; if (Case::Display::CONNECTION_TYPE::value) { - const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get()); + const auto displayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get()); ASSERT_TRUE(displayId); const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; ASSERT_TRUE(hwcDisplayId); @@ -137,10 +133,10 @@ void DisplayTransactionCommitTest::verifyPhysicalDisplayIsConnected() { EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); // SF should have a display token. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); + const auto displayIdOpt = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(displayIdOpt); - const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId); + const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(*displayIdOpt); ASSERT_TRUE(displayOpt); const auto& display = displayOpt->get(); @@ -246,9 +242,9 @@ void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() { EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); // SF should not have a PhysicalDisplay. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); - ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId)); + const auto physicalDisplayIdOpt = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(physicalDisplayIdOpt); + ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(*physicalDisplayIdOpt)); // The existing token should have been removed. verifyDisplayIsNotConnected(existing.token()); @@ -356,9 +352,10 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimar EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); // SF should not have a PhysicalDisplay. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); - ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId)); + const auto physicalDisplayIdOpt = + asPhysicalDisplayId(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(physicalDisplayIdOpt); + ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(*physicalDisplayIdOpt)); }(), testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected."); } @@ -400,10 +397,12 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimar // The existing token should have been removed. verifyDisplayIsNotConnected(existing.token()); - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); + const auto physicalDisplayIdOpt = + asPhysicalDisplayId(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(physicalDisplayIdOpt); - const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId); + const auto displayOpt = + mFlinger.mutablePhysicalDisplays().get(*physicalDisplayIdOpt); ASSERT_TRUE(displayOpt); EXPECT_NE(existing.token(), displayOpt->get().token()); @@ -540,9 +539,9 @@ TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayRemoval) { // Preconditions // A virtual display is set up but is removed from the current state. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId)); - mFlinger.mutableHwcDisplayData().try_emplace(displayId); + const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(displayId); + mFlinger.mutableHwcDisplayData().try_emplace(*displayId); Case::Display::injectHwcDisplay(this); auto existing = Case::Display::makeFakeExistingDisplayInjector(this); existing.inject(); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp index 19f8debe84..8972840c8a 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp @@ -38,30 +38,30 @@ TEST_F(FoldableTest, promotesPacesetterOnBoot) { ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); // ...and should still be after powering on. - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); } TEST_F(FoldableTest, promotesPacesetterOnFoldUnfold) { - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); // The outer display should become the pacesetter after folding. - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF); - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId); // The inner display should become the pacesetter after unfolding. - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF); - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); } TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) { - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); // The inner display should stay the pacesetter if both are powered on. // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates. - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); // The outer display should become the pacesetter if designated. @@ -74,20 +74,20 @@ TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) { } TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOff) { - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON); // The outer display should become the pacesetter if the inner display powers off. - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId); // The outer display should stay the pacesetter if both are powered on. // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates. - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId); // The inner display should become the pacesetter if the outer display powers off. - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::OFF); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); } @@ -114,8 +114,8 @@ TEST_F(FoldableTest, requestsHardwareVsyncForInnerDisplay) { .Times(0); // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to - // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal. - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); + // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode. + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); EXPECT_TRUE(mInnerDisplay->isPoweredOn()); EXPECT_FALSE(mOuterDisplay->isPoweredOn()); @@ -133,10 +133,10 @@ TEST_F(FoldableTest, requestsHardwareVsyncForOuterDisplay) { .Times(1); // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to - // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal. - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF); - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode. + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON); EXPECT_FALSE(mInnerDisplay->isPoweredOn()); EXPECT_TRUE(mOuterDisplay->isPoweredOn()); @@ -154,9 +154,9 @@ TEST_F(FoldableTest, requestsHardwareVsyncForBothDisplays) { .Times(1); // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to - // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal. - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode. + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON); EXPECT_TRUE(mInnerDisplay->isPoweredOn()); EXPECT_TRUE(mOuterDisplay->isPoweredOn()); @@ -167,18 +167,16 @@ TEST_F(FoldableTest, requestsHardwareVsyncForBothDisplays) { } TEST_F(FoldableTest, requestVsyncOnPowerOn) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true)) .Times(1); EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true)) .Times(1); - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON); } TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); // When the device boots, the inner display should be the pacesetter. ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); @@ -192,10 +190,10 @@ TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) { EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, false)) .Times(1); - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON); - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF); // Other display is now the pacesetter. ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp index b0cda0f270..b8b1b9527c 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp @@ -90,7 +90,9 @@ TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithIdentificationData) { mFlinger.configure(); EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID)); - EXPECT_TRUE(mFlinger.getHwComposer().isConnected(PrimaryDisplay::DISPLAY_ID::get())); + const auto primaryDisplayId = asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get()); + ASSERT_TRUE(primaryDisplayId); + EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*primaryDisplayId)); const auto primaryDisplayIdOpt = mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID); ASSERT_TRUE(primaryDisplayIdOpt.has_value()); @@ -98,13 +100,15 @@ TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithIdentificationData) { mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value()); ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value()); const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef(); - EXPECT_EQ(PrimaryDisplay::DISPLAY_ID::get(), primaryDisplaySnapshotRef.get().displayId()); + EXPECT_EQ(*primaryDisplayId, primaryDisplaySnapshotRef.get().displayId()); EXPECT_EQ(PrimaryDisplay::PORT::value, primaryDisplaySnapshotRef.get().port()); EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value, primaryDisplaySnapshotRef.get().connectionType()); EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); - EXPECT_TRUE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get())); + const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get()); + ASSERT_TRUE(externalDisplayId); + EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*externalDisplayId)); const auto externalDisplayIdOpt = mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID); ASSERT_TRUE(externalDisplayIdOpt.has_value()); @@ -112,7 +116,7 @@ TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithIdentificationData) { mFlinger.physicalDisplays().get(externalDisplayIdOpt.value()); ASSERT_TRUE(externalPhysicalDisplayOpt.has_value()); const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef(); - EXPECT_EQ(ExternalDisplay::DISPLAY_ID::get(), externalDisplaySnapshotRef.get().displayId()); + EXPECT_EQ(*externalDisplayId, externalDisplaySnapshotRef.get().displayId()); EXPECT_EQ(ExternalDisplay::PORT::value, externalDisplaySnapshotRef.get().port()); EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value, externalDisplaySnapshotRef.get().connectionType()); @@ -154,8 +158,8 @@ TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithoutIdentificationData) constexpr PhysicalDisplayId primaryInternalDisplayId = PhysicalDisplayId::fromPort(primaryInternalDisplayPort); EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID)); - ASSERT_EQ(primaryInternalDisplayId, PrimaryDisplay::DISPLAY_ID::get()); - EXPECT_TRUE(mFlinger.getHwComposer().isConnected(PrimaryDisplay::DISPLAY_ID::get())); + ASSERT_EQ(primaryInternalDisplayId, asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get())); + EXPECT_TRUE(mFlinger.getHwComposer().isConnected(primaryInternalDisplayId)); const auto primaryDisplayIdOpt = mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID); ASSERT_TRUE(primaryDisplayIdOpt.has_value()); @@ -220,12 +224,12 @@ TEST_F(HotplugTest, ignoresDuplicateDisconnection) { // The display should be scheduled for removal during the next commit. At this point, it should // still exist but be marked as disconnected. EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); - EXPECT_FALSE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get())); + const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get()); + ASSERT_TRUE(externalDisplayId); + EXPECT_FALSE(mFlinger.getHwComposer().isConnected(*externalDisplayId)); } TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { - SET_FLAG_FOR_TEST(flags::connected_display, true); - // Inject a primary display. PrimaryDisplayVariant::injectHwcDisplay(this); @@ -263,8 +267,6 @@ TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { } TEST_F(HotplugTest, rejectsHotplugOnActivePortsDuplicate) { - SET_FLAG_FOR_TEST(flags::connected_display, true); - // Inject a primary display. PrimaryDisplayVariant::injectHwcDisplay(this); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp index fed7b2e767..2332bf62da 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <android_companion_virtualdevice_flags.h> #include <com_android_graphics_surfaceflinger_flags.h> #include <common/test/FlagUtils.h> #include "DisplayTransactionTestHelpers.h" @@ -78,11 +79,19 @@ struct EventThreadBaseSupportedVariant { struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant { static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1); + setupDisableSyntheticVsyncCallExpectations(test); + } + + static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0); } static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1); + setupEnableSyntheticVsyncCallExpectations(test); + } + + static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0); } }; @@ -91,12 +100,20 @@ struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) { // Expect to enable hardware VSYNC and disable synthetic VSYNC. EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1); + setupDisableSyntheticVsyncCallExpectations(test); + } + + static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1); } static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) { // Expect to disable hardware VSYNC and enable synthetic VSYNC. EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1); + setupEnableSyntheticVsyncCallExpectations(test); + } + + static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1); } }; @@ -151,7 +168,7 @@ struct TransitionOffToDozeSuspendVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); - Case::EventThread::setupVsyncNoCallExpectations(test); + Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test); Case::setupRepaintEverythingCallExpectations(test); } @@ -176,7 +193,7 @@ struct TransitionDozeSuspendToOffVariant : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupVsyncNoCallExpectations(test); + Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); } @@ -188,7 +205,7 @@ struct TransitionDozeSuspendToOffVariant struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupVsyncNoCallExpectations(test); + Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); } }; @@ -206,7 +223,7 @@ struct TransitionDozeSuspendToDozeVariant struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupVsyncNoCallExpectations(test); + Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); } }; @@ -234,7 +251,7 @@ struct TransitionOnToUnknownVariant : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupVsyncNoCallExpectations(test); + Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test); Case::setupNoComposerPowerModeCallExpectations(test); } }; @@ -315,7 +332,7 @@ using ExternalDisplayPowerCase = EventThreadNotSupportedVariant, DispSyncNotSupportedVariant, TransitionVariant>; -class SetPowerModeInternalTest : public DisplayTransactionTest { +class SetPhysicalDisplayPowerModeTest : public DisplayTransactionTest { public: template <typename Case> void transitionDisplayCommon(); @@ -331,15 +348,17 @@ template <> struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {}; template <typename Case> -void SetPowerModeInternalTest::transitionDisplayCommon() { +void SetPhysicalDisplayPowerModeTest::transitionDisplayCommon() { // -------------------------------------------------------------------- // Preconditions + SET_FLAG_FOR_TEST(android::companion::virtualdevice::flags::correct_virtual_display_power_state, + true); + Case::Doze::setupComposerCallExpectations(this); auto display = Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE); - auto displayId = display->getId(); - if (auto physicalDisplayId = PhysicalDisplayId::tryCast(displayId)) { + if (auto physicalDisplayId = asPhysicalDisplayId(display->getDisplayIdVariant())) { Case::setInitialHwVsyncEnabled(this, *physicalDisplayId, PowerModeInitialVSyncEnabled< Case::Transition::INITIAL_POWER_MODE>::value); @@ -353,7 +372,7 @@ void SetPowerModeInternalTest::transitionDisplayCommon() { // -------------------------------------------------------------------- // Invocation - mFlinger.setPowerModeInternal(display, Case::Transition::TARGET_POWER_MODE); + mFlinger.setPhysicalDisplayPowerMode(display, Case::Transition::TARGET_POWER_MODE); // -------------------------------------------------------------------- // Postconditions @@ -361,7 +380,7 @@ void SetPowerModeInternalTest::transitionDisplayCommon() { Case::Transition::verifyPostconditions(this); } -TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { +TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfNoChange) { using Case = SimplePrimaryDisplayCase; // -------------------------------------------------------------------- @@ -378,7 +397,7 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { // -------------------------------------------------------------------- // Invocation - mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON); + mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::ON); // -------------------------------------------------------------------- // Postconditions @@ -386,16 +405,16 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); } -TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfVirtualDisplay) { using Case = HwcVirtualDisplayCase; // -------------------------------------------------------------------- // Preconditions // Insert display data so that the HWC thinks it created the virtual display. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId)); - mFlinger.mutableHwcDisplayData().try_emplace(displayId); + const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(displayId); + ASSERT_TRUE(mFlinger.mutableHwcDisplayData().try_emplace(*displayId).second); // A virtual display device is set up Case::Display::injectHwcDisplay(this); @@ -408,7 +427,7 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay // -------------------------------------------------------------------- // Invocation - mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF); + mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::OFF); // -------------------------------------------------------------------- // Postconditions @@ -416,88 +435,83 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozePrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) { transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) { - SET_FLAG_FOR_TEST(flags::multithreaded_present, true); +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); } -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) { +TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownExternalDisplay) { transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>(); } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp index cd554ea1ec..23e73de660 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp @@ -235,7 +235,7 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { constexpr auto kConnectionTypeOpt = Case::Display::CONNECTION_TYPE::value; if constexpr (kConnectionTypeOpt) { - const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get()); + const auto displayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get()); ASSERT_TRUE(displayId); const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; ASSERT_TRUE(hwcDisplayId); @@ -282,7 +282,7 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { // Postconditions ASSERT_NE(nullptr, device); - EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId()); + EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getDisplayIdVariant()); EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual()); EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure()); EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary()); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 41f2b6e5c0..c5973db109 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -337,9 +337,9 @@ public: mFlinger->configure(); } - void configureAndCommit() { + void configureAndCommit(bool modeset = false) { configure(); - commitTransactionsLocked(eDisplayTransactionNeeded); + commitTransactionsLocked(eDisplayTransactionNeeded, modeset); } void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime, @@ -399,12 +399,16 @@ public: float requestedRefreshRate = 0.0f) { static const std::string kTestId = "virtual:libsurfaceflinger_unittest:TestableSurfaceFlinger"; - return mFlinger->createVirtualDisplay(displayName, isSecure, kTestId, requestedRefreshRate); + return mFlinger + ->createVirtualDisplay(displayName, isSecure, + gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower, + kTestId, requestedRefreshRate); } auto createVirtualDisplay(const std::string& displayName, bool isSecure, + gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId, float requestedRefreshRate = 0.0f) { - return mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId, + return mFlinger->createVirtualDisplay(displayName, isSecure, optimizationPolicy, uniqueId, requestedRefreshRate); } @@ -429,11 +433,14 @@ public: dispSurface, producer); } - void commitTransactionsLocked(uint32_t transactionFlags) { + void commitTransactionsLocked(uint32_t transactionFlags, bool modeset = false) { Mutex::Autolock lock(mFlinger->mStateLock); ftl::FakeGuard guard(kMainThreadContext); mFlinger->processDisplayChangesLocked(); mFlinger->commitTransactionsLocked(transactionFlags); + if (modeset) { + mFlinger->initiateDisplayModeChanges(); + } } void onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, DisplayHotplugEvent event) { @@ -455,9 +462,9 @@ public: } // 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 setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display, + hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->setPhysicalDisplayPowerMode(display, mode); } auto renderScreenImpl(const sp<DisplayDevice> display, const Rect sourceCrop, @@ -474,7 +481,7 @@ public: SurfaceFlinger::ScreenshotArgs screenshotArgs; screenshotArgs.captureTypeVariant = display; - screenshotArgs.displayId = std::nullopt; + screenshotArgs.displayIdVariant = std::nullopt; screenshotArgs.sourceCrop = sourceCrop; screenshotArgs.reqSize = sourceCrop.getSize(); screenshotArgs.dataspace = dataspace; @@ -576,7 +583,7 @@ public: } sp<DisplayDevice> createVirtualDisplayDevice(const sp<IBinder> displayToken, - VirtualDisplayId displayId, + GpuVirtualDisplayId displayId, float requestedRefreshRate) { constexpr ui::Size kResolution = {1080, 1920}; auto compositionDisplay = compositionengine::impl:: @@ -713,6 +720,7 @@ public: } auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; } + auto& mutableMaxAcquiredBuffers() { return SurfaceFlinger::maxAcquiredBuffersOpt; } auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mLayerSnapshotBuilder; } @@ -819,9 +827,11 @@ public: static constexpr int32_t DEFAULT_DPI = 320; static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0; - FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType, + FakeHwcDisplayInjector(DisplayIdVariant displayIdVariant, hal::DisplayType hwcDisplayType, bool isPrimary) - : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {} + : mDisplayIdVariant(displayIdVariant), + mHwcDisplayType(hwcDisplayType), + mIsPrimary(isPrimary) {} auto& setHwcDisplayId(hal::HWDisplayId displayId) { mHwcDisplayId = displayId; @@ -886,7 +896,9 @@ public: display->setPowerMode(mPowerMode); - flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display); + const auto halDisplayId = asHalDisplayId(mDisplayIdVariant); + ASSERT_TRUE(halDisplayId); + flinger->mutableHwcDisplayData()[*halDisplayId].hwcDisplay = std::move(display); EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _)) .WillRepeatedly( @@ -924,9 +936,10 @@ public: DoAll(SetArgPointee<3>(mConfigGroup), Return(hal::Error::NONE))); if (mHwcDisplayType == hal::DisplayType::PHYSICAL) { - const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId); - LOG_ALWAYS_FATAL_IF(!physicalId); - flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId); + const auto physicalDisplayId = asPhysicalDisplayId(mDisplayIdVariant); + ASSERT_TRUE(physicalDisplayId); + flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, + *physicalDisplayId); if (mIsPrimary) { flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId; } else { @@ -939,7 +952,7 @@ public: } private: - const HalDisplayId mDisplayId; + const DisplayIdVariant mDisplayIdVariant; const hal::DisplayType mHwcDisplayType; const bool mIsPrimary; @@ -975,8 +988,8 @@ public: sp<IBinder> token() const { return mDisplayToken; } auto physicalDisplay() const { - return ftl::Optional(mCreationArgs.compositionDisplay->getDisplayId()) - .and_then(&PhysicalDisplayId::tryCast) + return mCreationArgs.compositionDisplay->getDisplayIdVariant() + .and_then(asPhysicalDisplayId) .and_then(display::getPhysicalDisplay(mFlinger.physicalDisplays())); } @@ -1074,7 +1087,9 @@ public: DisplayDeviceState state; state.isSecure = mCreationArgs.isSecure; - if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) { + if (const auto physicalId = + mCreationArgs.compositionDisplay->getDisplayIdVariant().and_then( + asPhysicalDisplayId)) { LOG_ALWAYS_FATAL_IF(!mConnectionType); LOG_ALWAYS_FATAL_IF(!mHwcDisplayId); diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 918107d270..ccf6a9c7f6 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -304,6 +304,53 @@ TEST_F(VSyncPredictorTest, againstOutliersDiscontinuous_500hzLowVariance) { EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError)); } +TEST_F(VSyncPredictorTest, recoverAfterDriftedVSyncAreReplacedWithCorrectVSync) { + SET_FLAG_FOR_TEST(flags::vsync_predictor_recovery, true); + auto constexpr idealPeriodNs = 4166666; + auto constexpr minFrameIntervalNs = 8333333; + auto constexpr idealPeriod = Fps::fromPeriodNsecs(idealPeriodNs); + auto constexpr minFrameRate = Fps::fromPeriodNsecs(minFrameIntervalNs); + hal::VrrConfig vrrConfig{.minFrameIntervalNs = minFrameIntervalNs}; + ftl::NonNull<DisplayModePtr> mode = + ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), idealPeriod, vrrConfig)); + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ true); + // Curated list of VSyncs that causes the VSync drift. + std::vector<nsecs_t> const simulatedVsyncs{74473665741, 74481774375, 74489911818, 74497993491, + 74506000833, 74510002150, 74513904390, 74517748707, + 74521550947, 74525383187, 74529165427, 74533067667, + 74536751484, 74540653724, 74544282649, 74548084889, + 74551917129, 74555699369, 74559601609, 74563601611, + 74567503851, 74571358168, 74575260408, 74578889333, + 74582691573, 74586523813, 74590306053, 74593589870, + 74597492110, 74601121035, 74604923275, 74608755515, + 74612537755, 74616166680, 74619795605, 74623424530, + 74627043455, 74630645695, 74634245935, 74637778175, + 74641291992, 74644794232, 74648275157, 74651575397, + 74654807637, 74658007877, 74661176117, 74664345357}; + for (auto const& timestamp : simulatedVsyncs) { + vrrTracker.addVsyncTimestamp(timestamp); + } + auto slope = vrrTracker.getVSyncPredictionModel().slope; + // Without using the idealPeriod for the calculation of the VSync predictor mode the + // the slope would be 3343031 + EXPECT_THAT(slope, IsCloseTo(4347805, mMaxRoundingError)); + EXPECT_FALSE(vrrTracker.needsMoreSamples()); + + auto lastVsync = 74664345357; + // Add valid VSyncs to replace the drifted VSyncs + for (int i = 0; i <= kHistorySize; i++) { + lastVsync += minFrameIntervalNs; + EXPECT_TRUE(vrrTracker.addVsyncTimestamp(lastVsync)); + } + EXPECT_FALSE(vrrTracker.needsMoreSamples()); + slope = vrrTracker.getVSyncPredictionModel().slope; + // Corrected slop is closer to the idealPeriod + // when valid vsync are inserted otherwise this would still be 3349673 + EXPECT_THAT(slope, IsCloseTo(idealPeriodNs, mMaxRoundingError)); +} + TEST_F(VSyncPredictorTest, handlesVsyncChange) { auto const fastPeriod = 100; auto const fastTimeBase = 100; @@ -402,11 +449,7 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { std::vector<nsecs_t> const simulatedVsyncs{ 158929578733000, 158929306806205, // oldest TS in ringbuffer - 158929650879052, - 158929661969209, - 158929684198847, - 158929695268171, - 158929706370359, + 158929650879052, 158929661969209, 158929684198847, 158929695268171, 158929706370359, }; auto const idealPeriod = 11111111; auto const expectedPeriod = 11113919; @@ -421,9 +464,9 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError)); EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError)); - // (timePoint - oldestTS) % expectedPeriod works out to be: 395334 - // (timePoint - oldestTS) / expectedPeriod works out to be: 38.96 - // so failure to account for the offset will floor the ordinal to 38, which was in the past. + // (timePoint - oldestTS) % expectedPeriod works out to be: 10702663 + // (timePoint - oldestTS) / expectedPeriod works out to be: 37.96 + // so failure to account for the offset will floor the ordinal to 37, which was in the past. auto const timePoint = 158929728723871; auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint); EXPECT_THAT(prediction, Ge(timePoint)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 3036fec456..cce4d2aba8 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -55,7 +55,6 @@ public: MOCK_METHOD(void, onHdcpLevelsChanged, (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel), (override)); - MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override)); }; } // namespace android::mock diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp index 4735ae5897..4b12cc20a4 100644 --- a/services/vibratorservice/Android.bp +++ b/services/vibratorservice/Android.bp @@ -26,6 +26,7 @@ cc_library_shared { srcs: [ "VibratorCallbackScheduler.cpp", + "VibratorController.cpp", "VibratorHalController.cpp", "VibratorHalWrapper.cpp", "VibratorManagerHalController.cpp", @@ -41,22 +42,17 @@ cc_library_shared { }, shared_libs: [ + "android.hardware.vibrator-V3-ndk", "libbinder_ndk", - "libhidlbase", "liblog", "libutils", - "android.hardware.vibrator-V3-ndk", - "android.hardware.vibrator@1.0", - "android.hardware.vibrator@1.1", - "android.hardware.vibrator@1.2", - "android.hardware.vibrator@1.3", ], cflags: [ "-Wall", "-Werror", - "-Wunused", "-Wunreachable-code", + "-Wunused", ], local_include_dirs: ["include"], diff --git a/services/vibratorservice/VibratorController.cpp b/services/vibratorservice/VibratorController.cpp new file mode 100644 index 0000000000..21924e9f13 --- /dev/null +++ b/services/vibratorservice/VibratorController.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2025 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 "VibratorController" + +#ifndef qDoWithRetries +#define qDoWithRetries(op) doWithRetries(op, __FUNCTION__) +#endif + +#include <aidl/android/hardware/vibrator/IVibrator.h> +#include <android/binder_manager.h> +#include <binder/IServiceManager.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorController.h> + +using ::aidl::android::hardware::vibrator::Effect; +using ::aidl::android::hardware::vibrator::EffectStrength; +using ::aidl::android::hardware::vibrator::IVibrator; + +using Status = ::ndk::ScopedAStatus; + +using namespace std::placeholders; + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +inline bool isStatusUnsupported(const Status& status) { + // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported. + return status.getStatus() == STATUS_UNKNOWN_TRANSACTION || + status.getExceptionCode() == EX_UNSUPPORTED_OPERATION; +} + +inline bool isStatusTransactionFailed(const Status& status) { + // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported. + return status.getStatus() != STATUS_UNKNOWN_TRANSACTION && + status.getExceptionCode() == EX_TRANSACTION_FAILED; +} + +// ------------------------------------------------------------------------------------------------- + +bool VibratorProvider::isDeclared() { + std::lock_guard<std::mutex> lock(mMutex); + if (mIsDeclared.has_value()) { + return *mIsDeclared; + } + + bool isDeclared = AServiceManager_isDeclared(mServiceName.c_str()); + if (!isDeclared) { + ALOGV("Vibrator HAL service not declared."); + } + + mIsDeclared.emplace(isDeclared); + return isDeclared; +} + +std::shared_ptr<IVibrator> VibratorProvider::waitForVibrator() { + if (!isDeclared()) { + return nullptr; + } + + auto vibrator = IVibrator::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(mServiceName.c_str()))); + if (vibrator) { + ALOGV("Successfully connected to Vibrator HAL service."); + } else { + ALOGE("Error connecting to declared Vibrator HAL service."); + } + + return vibrator; +} + +std::shared_ptr<IVibrator> VibratorProvider::checkForVibrator() { + if (!isDeclared()) { + return nullptr; + } + + auto vibrator = IVibrator::fromBinder( + ndk::SpAIBinder(AServiceManager_checkService(mServiceName.c_str()))); + if (vibrator) { + ALOGV("Successfully reconnected to Vibrator HAL service."); + } else { + ALOGE("Error reconnecting to declared Vibrator HAL service."); + } + + return vibrator; +} + +// ------------------------------------------------------------------------------------------------- + +bool VibratorController::init() { + if (!mVibratorProvider->isDeclared()) { + return false; + } + std::lock_guard<std::mutex> lock(mMutex); + if (mVibrator == nullptr) { + mVibrator = mVibratorProvider->waitForVibrator(); + } + return mVibratorProvider->isDeclared(); +} + +Status VibratorController::off() { + return qDoWithRetries(std::bind(&IVibrator::off, _1)); +} + +Status VibratorController::setAmplitude(float amplitude) { + return qDoWithRetries(std::bind(&IVibrator::setAmplitude, _1, amplitude)); +} + +Status VibratorController::setExternalControl(bool enabled) { + return qDoWithRetries(std::bind(&IVibrator::setExternalControl, _1, enabled)); +} + +Status VibratorController::alwaysOnEnable(int32_t id, const Effect& effect, + const EffectStrength& strength) { + return qDoWithRetries(std::bind(&IVibrator::alwaysOnEnable, _1, id, effect, strength)); +} + +Status VibratorController::alwaysOnDisable(int32_t id) { + return qDoWithRetries(std::bind(&IVibrator::alwaysOnDisable, _1, id)); +} + +// ------------------------------------------------------------------------------------------------- + +std::shared_ptr<IVibrator> VibratorController::reconnectToVibrator() { + std::lock_guard<std::mutex> lock(mMutex); + mVibrator = mVibratorProvider->checkForVibrator(); + return mVibrator; +} + +Status VibratorController::doWithRetries(const VibratorController::VibratorOp& op, + const char* logLabel) { + if (!init()) { + ALOGV("Skipped %s because Vibrator HAL is not declared", logLabel); + return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "IVibrator not declared"); + } + std::shared_ptr<IVibrator> vibrator; + { + std::lock_guard<std::mutex> lock(mMutex); + vibrator = mVibrator; + } + + if (!vibrator) { + ALOGE("Skipped %s because Vibrator HAL is declared but failed to load", logLabel); + return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, + "IVibrator declared but failed to load"); + } + + auto status = doOnce(vibrator.get(), op, logLabel); + for (int i = 1; i < MAX_ATTEMPTS && isStatusTransactionFailed(status); i++) { + vibrator = reconnectToVibrator(); + if (!vibrator) { + // Failed to reconnect to vibrator HAL after a transaction failed, skip retries. + break; + } + status = doOnce(vibrator.get(), op, logLabel); + } + + return status; +} + +Status VibratorController::doOnce(IVibrator* vibrator, const VibratorController::VibratorOp& op, + const char* logLabel) { + auto status = op(vibrator); + if (!status.isOk()) { + if (isStatusUnsupported(status)) { + ALOGV("Vibrator HAL %s is unsupported: %s", logLabel, status.getMessage()); + } else { + ALOGE("Vibrator HAL %s failed: %s", logLabel, status.getMessage()); + } + } + return status; +} + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp index 283a5f0301..302e3e1299 100644 --- a/services/vibratorservice/VibratorHalController.cpp +++ b/services/vibratorservice/VibratorHalController.cpp @@ -18,8 +18,6 @@ #include <aidl/android/hardware/vibrator/IVibrator.h> #include <android/binder_manager.h> -#include <android/hardware/vibrator/1.3/IVibrator.h> -#include <hardware/vibrator.h> #include <utils/Log.h> @@ -31,15 +29,10 @@ using aidl::android::hardware::vibrator::CompositeEffect; using aidl::android::hardware::vibrator::CompositePrimitive; using aidl::android::hardware::vibrator::Effect; using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::IVibrator; using std::chrono::milliseconds; -namespace V1_0 = android::hardware::vibrator::V1_0; -namespace V1_1 = android::hardware::vibrator::V1_1; -namespace V1_2 = android::hardware::vibrator::V1_2; -namespace V1_3 = android::hardware::vibrator::V1_3; -namespace Aidl = aidl::android::hardware::vibrator; - namespace android { namespace vibrator { @@ -53,9 +46,9 @@ std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> schedu return nullptr; } - auto serviceName = std::string(Aidl::IVibrator::descriptor) + "/default"; + auto serviceName = std::string(IVibrator::descriptor) + "/default"; if (AServiceManager_isDeclared(serviceName.c_str())) { - std::shared_ptr<Aidl::IVibrator> hal = Aidl::IVibrator::fromBinder( + std::shared_ptr<IVibrator> hal = IVibrator::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str()))); if (hal) { ALOGV("Successfully connected to Vibrator HAL AIDL service."); @@ -63,30 +56,9 @@ std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> schedu } } - sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService(); - if (halV1_0 == nullptr) { - ALOGV("Vibrator HAL service not available."); - gHalExists = false; - return nullptr; - } - - sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0); - if (halV1_3) { - ALOGV("Successfully connected to Vibrator HAL v1.3 service."); - return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3); - } - sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0); - if (halV1_2) { - ALOGV("Successfully connected to Vibrator HAL v1.2 service."); - return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2); - } - sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0); - if (halV1_1) { - ALOGV("Successfully connected to Vibrator HAL v1.1 service."); - return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1); - } - ALOGV("Successfully connected to Vibrator HAL v1.0 service."); - return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0); + ALOGV("Vibrator HAL service not available."); + gHalExists = false; + return nullptr; } // ------------------------------------------------------------------------------------------------- diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp index 536a6b352b..5d4c17dc76 100644 --- a/services/vibratorservice/VibratorHalWrapper.cpp +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "VibratorHalWrapper" #include <aidl/android/hardware/vibrator/IVibrator.h> -#include <android/hardware/vibrator/1.3/IVibrator.h> #include <hardware/vibrator.h> #include <cmath> @@ -33,32 +32,18 @@ using aidl::android::hardware::vibrator::CompositePwleV2; using aidl::android::hardware::vibrator::Effect; using aidl::android::hardware::vibrator::EffectStrength; using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry; +using aidl::android::hardware::vibrator::IVibrator; using aidl::android::hardware::vibrator::PrimitivePwle; using aidl::android::hardware::vibrator::VendorEffect; using std::chrono::milliseconds; -namespace V1_0 = android::hardware::vibrator::V1_0; -namespace V1_1 = android::hardware::vibrator::V1_1; -namespace V1_2 = android::hardware::vibrator::V1_2; -namespace V1_3 = android::hardware::vibrator::V1_3; -namespace Aidl = aidl::android::hardware::vibrator; - namespace android { namespace vibrator { // ------------------------------------------------------------------------------------------------- -template <class T> -bool isStaticCastValid(Effect effect) { - T castEffect = static_cast<T>(effect); - auto iter = hardware::hidl_enum_range<T>(); - return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end()); -} - -// ------------------------------------------------------------------------------------------------- - Info HalWrapper::getInfo() { getCapabilities(); getPrimitiveDurations(); @@ -261,7 +246,7 @@ void AidlHalWrapper::tryReconnect() { if (!result.isOk()) { return; } - std::shared_ptr<Aidl::IVibrator> newHandle = result.value(); + std::shared_ptr<IVibrator> newHandle = result.value(); if (newHandle) { std::lock_guard<std::mutex> lock(mHandleMutex); mHandle = std::move(newHandle); @@ -514,219 +499,13 @@ AidlHalWrapper::getFrequencyToOutputAccelerationMapInternal() { frequencyToOutputAccelerationMap); } -std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() { - std::lock_guard<std::mutex> lock(mHandleMutex); - return mHandle; -} - -// ------------------------------------------------------------------------------------------------- - -template <typename I> -HalResult<void> HidlHalWrapper<I>::ping() { - return HalResultFactory::fromReturn(getHal()->ping()); -} - -template <typename I> -void HidlHalWrapper<I>::tryReconnect() { - sp<I> newHandle = I::tryGetService(); - if (newHandle) { - std::lock_guard<std::mutex> lock(mHandleMutex); - mHandle = std::move(newHandle); - } -} - -template <typename I> -HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout, - const std::function<void()>& completionCallback) { - auto status = getHal()->on(timeout.count()); - auto ret = HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR)); - if (ret.isOk()) { - mCallbackScheduler->schedule(completionCallback, timeout); - } - return ret; -} - -template <typename I> -HalResult<void> HidlHalWrapper<I>::off() { - auto status = getHal()->off(); - return HalResultFactory::fromStatus(status.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 status = getHal()->setAmplitude(amp); - return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR)); -} - -template <typename I> -HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) { - ALOGV("Skipped setExternalControl because Vibrator HAL does not support it"); - return HalResult<void>::unsupported(); -} - -template <typename I> -HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) { - ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available"); - return HalResult<void>::unsupported(); -} - -template <typename I> -HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) { - ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available"); - return HalResult<void>::unsupported(); -} - -template <typename I> -HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() { - hardware::Return<bool> result = getHal()->supportsAmplitudeControl(); - Capabilities capabilities = - result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE; - return HalResultFactory::fromReturn<Capabilities>(std::move(result), capabilities); -} - -template <typename I> -template <typename T> -HalResult<milliseconds> HidlHalWrapper<I>::performInternal( - perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength, - const std::function<void()>& completionCallback) { - V1_0::Status status; - int32_t lengthMs; - auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) { - status = retStatus; - lengthMs = retLengthMs; - }; - - V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength); - auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback); - milliseconds length = milliseconds(lengthMs); - - auto ret = HalResultFactory::fromReturn<milliseconds>(std::move(result), status, length); - if (ret.isOk()) { - mCallbackScheduler->schedule(completionCallback, length); - } - - return ret; -} - -template <typename I> -sp<I> HidlHalWrapper<I>::getHal() { +std::shared_ptr<IVibrator> AidlHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; } // ------------------------------------------------------------------------------------------------- -HalResult<milliseconds> HidlHalWrapperV1_0::performEffect( - Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { - if (isStaticCastValid<V1_0::Effect>(effect)) { - return performInternal(&V1_0::IVibrator::perform, getHal(), - static_cast<V1_0::Effect>(effect), strength, completionCallback); - } - - ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", - Aidl::toString(effect).c_str()); - return HalResult<milliseconds>::unsupported(); -} - -// ------------------------------------------------------------------------------------------------- - -HalResult<milliseconds> HidlHalWrapperV1_1::performEffect( - Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { - if (isStaticCastValid<V1_0::Effect>(effect)) { - return performInternal(&V1_1::IVibrator::perform, getHal(), - static_cast<V1_0::Effect>(effect), strength, completionCallback); - } - if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { - return performInternal(&V1_1::IVibrator::perform_1_1, getHal(), - static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback); - } - - ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", - Aidl::toString(effect).c_str()); - return HalResult<milliseconds>::unsupported(); -} - -// ------------------------------------------------------------------------------------------------- - -HalResult<milliseconds> HidlHalWrapperV1_2::performEffect( - Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { - if (isStaticCastValid<V1_0::Effect>(effect)) { - return performInternal(&V1_2::IVibrator::perform, getHal(), - static_cast<V1_0::Effect>(effect), strength, completionCallback); - } - if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { - return performInternal(&V1_2::IVibrator::perform_1_1, getHal(), - static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback); - } - if (isStaticCastValid<V1_2::Effect>(effect)) { - return performInternal(&V1_2::IVibrator::perform_1_2, getHal(), - static_cast<V1_2::Effect>(effect), strength, completionCallback); - } - - ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", - Aidl::toString(effect).c_str()); - return HalResult<milliseconds>::unsupported(); -} - -// ------------------------------------------------------------------------------------------------- - -HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) { - auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled)); - return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); -} - -HalResult<milliseconds> HidlHalWrapperV1_3::performEffect( - Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { - if (isStaticCastValid<V1_0::Effect>(effect)) { - return performInternal(&V1_3::IVibrator::perform, getHal(), - static_cast<V1_0::Effect>(effect), strength, completionCallback); - } - if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { - return performInternal(&V1_3::IVibrator::perform_1_1, getHal(), - static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback); - } - if (isStaticCastValid<V1_2::Effect>(effect)) { - return performInternal(&V1_3::IVibrator::perform_1_2, getHal(), - static_cast<V1_2::Effect>(effect), strength, completionCallback); - } - if (isStaticCastValid<V1_3::Effect>(effect)) { - return performInternal(&V1_3::IVibrator::perform_1_3, getHal(), - static_cast<V1_3::Effect>(effect), strength, completionCallback); - } - - ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", - Aidl::toString(effect).c_str()); - return HalResult<milliseconds>::unsupported(); -} - -HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() { - Capabilities capabilities = Capabilities::NONE; - - sp<V1_3::IVibrator> hal = getHal(); - auto amplitudeResult = hal->supportsAmplitudeControl(); - if (!amplitudeResult.isOk()) { - return HalResultFactory::fromReturn<Capabilities>(std::move(amplitudeResult), capabilities); - } - - auto externalControlResult = hal->supportsExternalControl(); - if (amplitudeResult.withDefault(false)) { - capabilities |= Capabilities::AMPLITUDE_CONTROL; - } - if (externalControlResult.withDefault(false)) { - capabilities |= Capabilities::EXTERNAL_CONTROL; - - if (amplitudeResult.withDefault(false)) { - capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL; - } - } - - return HalResultFactory::fromReturn<Capabilities>(std::move(externalControlResult), - capabilities); -} - -// ------------------------------------------------------------------------------------------------- - }; // namespace vibrator }; // namespace android diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp index 494f88f10e..31b6ed030f 100644 --- a/services/vibratorservice/VibratorManagerHalController.cpp +++ b/services/vibratorservice/VibratorManagerHalController.cpp @@ -20,7 +20,10 @@ #include <vibratorservice/VibratorManagerHalController.h> -namespace Aidl = aidl::android::hardware::vibrator; +using aidl::android::hardware::vibrator::IVibrationSession; +using aidl::android::hardware::vibrator::IVibrator; +using aidl::android::hardware::vibrator::IVibratorManager; +using aidl::android::hardware::vibrator::VibrationSessionConfig; namespace android { @@ -29,9 +32,9 @@ namespace vibrator { std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler) { static bool gHalExists = true; if (gHalExists) { - auto serviceName = std::string(Aidl::IVibratorManager::descriptor) + "/default"; + auto serviceName = std::string(IVibratorManager::descriptor) + "/default"; if (AServiceManager_isDeclared(serviceName.c_str())) { - std::shared_ptr<Aidl::IVibratorManager> hal = Aidl::IVibratorManager::fromBinder( + std::shared_ptr<IVibratorManager> hal = IVibratorManager::fromBinder( ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str()))); if (hal) { ALOGV("Successfully connected to VibratorManager HAL AIDL service."); @@ -41,6 +44,7 @@ std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackSch } } + ALOGV("VibratorManager HAL service not available."); gHalExists = false; return std::make_shared<LegacyManagerHalWrapper>(); } @@ -150,10 +154,10 @@ HalResult<void> ManagerHalController::cancelSynced() { return apply(cancelSyncedFn, "cancelSynced"); } -HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalController::startSession( - const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config, +HalResult<std::shared_ptr<IVibrationSession>> ManagerHalController::startSession( + const std::vector<int32_t>& ids, const VibrationSessionConfig& config, const std::function<void()>& completionCallback) { - hal_fn<std::shared_ptr<Aidl::IVibrationSession>> startSessionFn = + hal_fn<std::shared_ptr<IVibrationSession>> startSessionFn = [&](std::shared_ptr<ManagerHalWrapper> hal) { return hal->startSession(ids, config, completionCallback); }; diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp index 3db8ff8699..bab3f58754 100644 --- a/services/vibratorservice/VibratorManagerHalWrapper.cpp +++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp @@ -20,7 +20,10 @@ #include <vibratorservice/VibratorManagerHalWrapper.h> -namespace Aidl = aidl::android::hardware::vibrator; +using aidl::android::hardware::vibrator::IVibrationSession; +using aidl::android::hardware::vibrator::IVibrator; +using aidl::android::hardware::vibrator::IVibratorManager; +using aidl::android::hardware::vibrator::VibrationSessionConfig; namespace android { @@ -41,10 +44,9 @@ HalResult<void> ManagerHalWrapper::cancelSynced() { return HalResult<void>::unsupported(); } -HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalWrapper::startSession( - const std::vector<int32_t>&, const Aidl::VibrationSessionConfig&, - const std::function<void()>&) { - return HalResult<std::shared_ptr<Aidl::IVibrationSession>>::unsupported(); +HalResult<std::shared_ptr<IVibrationSession>> ManagerHalWrapper::startSession( + const std::vector<int32_t>&, const VibrationSessionConfig&, const std::function<void()>&) { + return HalResult<std::shared_ptr<IVibrationSession>>::unsupported(); } HalResult<void> ManagerHalWrapper::clearSessions() { @@ -87,11 +89,11 @@ HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(i std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator( int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) { - std::function<HalResult<std::shared_ptr<Aidl::IVibrator>>()> reconnectFn = [=, this]() { - std::shared_ptr<Aidl::IVibrator> vibrator; + std::function<HalResult<std::shared_ptr<IVibrator>>()> reconnectFn = [=, this]() { + std::shared_ptr<IVibrator> vibrator; auto status = this->getHal()->getVibrator(vibratorId, &vibrator); - return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrator>>(std::move(status), - vibrator); + return HalResultFactory::fromStatus<std::shared_ptr<IVibrator>>(std::move(status), + vibrator); }; auto result = reconnectFn(); if (!result.isOk()) { @@ -110,8 +112,8 @@ HalResult<void> AidlManagerHalWrapper::ping() { } void AidlManagerHalWrapper::tryReconnect() { - auto aidlServiceName = std::string(Aidl::IVibratorManager::descriptor) + "/default"; - std::shared_ptr<Aidl::IVibratorManager> newHandle = Aidl::IVibratorManager::fromBinder( + auto aidlServiceName = std::string(IVibratorManager::descriptor) + "/default"; + std::shared_ptr<IVibratorManager> newHandle = IVibratorManager::fromBinder( ndk::SpAIBinder(AServiceManager_checkService(aidlServiceName.c_str()))); if (newHandle) { std::lock_guard<std::mutex> lock(mHandleMutex); @@ -198,15 +200,14 @@ HalResult<void> AidlManagerHalWrapper::triggerSynced( return HalResultFactory::fromStatus(getHal()->triggerSynced(cb)); } -HalResult<std::shared_ptr<Aidl::IVibrationSession>> AidlManagerHalWrapper::startSession( - const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config, +HalResult<std::shared_ptr<IVibrationSession>> AidlManagerHalWrapper::startSession( + const std::vector<int32_t>& ids, const VibrationSessionConfig& config, const std::function<void()>& completionCallback) { auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback); - std::shared_ptr<Aidl::IVibrationSession> session; + std::shared_ptr<IVibrationSession> session; auto status = getHal()->startSession(ids, config, cb, &session); - return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrationSession>>(std::move(status), - std::move( - session)); + return HalResultFactory::fromStatus<std::shared_ptr<IVibrationSession>>(std::move(status), + std::move(session)); } HalResult<void> AidlManagerHalWrapper::cancelSynced() { @@ -227,7 +228,7 @@ HalResult<void> AidlManagerHalWrapper::clearSessions() { return HalResultFactory::fromStatus(getHal()->clearSessions()); } -std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() { +std::shared_ptr<IVibratorManager> AidlManagerHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; } diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp index 915d6c7bc3..6fc5cf3517 100644 --- a/services/vibratorservice/benchmarks/Android.bp +++ b/services/vibratorservice/benchmarks/Android.bp @@ -29,15 +29,10 @@ cc_benchmark { ], shared_libs: [ "libbinder_ndk", - "libhidlbase", "liblog", "libutils", "libvibratorservice", "android.hardware.vibrator-V3-ndk", - "android.hardware.vibrator@1.0", - "android.hardware.vibrator@1.1", - "android.hardware.vibrator@1.2", - "android.hardware.vibrator@1.3", ], cflags: [ "-Wall", diff --git a/services/vibratorservice/include/vibratorservice/VibratorController.h b/services/vibratorservice/include/vibratorservice/VibratorController.h new file mode 100644 index 0000000000..691c8ae518 --- /dev/null +++ b/services/vibratorservice/include/vibratorservice/VibratorController.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_VIBRATOR_CONTROLLER_H +#define ANDROID_OS_VIBRATOR_CONTROLLER_H + +#include <aidl/android/hardware/vibrator/IVibrator.h> + +#include <android-base/thread_annotations.h> + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +/* Provider for IVibrator HAL service instances. */ +class VibratorProvider { +public: + using IVibrator = ::aidl::android::hardware::vibrator::IVibrator; + + VibratorProvider() : mServiceName(std::string(IVibrator::descriptor) + "/default") {} + virtual ~VibratorProvider() = default; + + /* Returns true if vibrator HAL service is declared in the device, false otherwise. */ + virtual bool isDeclared(); + + /* Connects to vibrator HAL, possibly waiting for the declared service to become available. */ + virtual std::shared_ptr<IVibrator> waitForVibrator(); + + /* Connects to vibrator HAL if declared and available, without waiting. */ + virtual std::shared_ptr<IVibrator> checkForVibrator(); + +private: + std::mutex mMutex; + const std::string mServiceName; + std::optional<bool> mIsDeclared GUARDED_BY(mMutex); +}; + +// ------------------------------------------------------------------------------------------------- + +/* Controller for Vibrator HAL handle. + * This relies on VibratorProvider to connect to the underlying Vibrator HAL service and reconnects + * after each transaction failed call. This also ensures connecting to the service is thread-safe. + */ +class VibratorController { +public: + using Effect = ::aidl::android::hardware::vibrator::Effect; + using EffectStrength = ::aidl::android::hardware::vibrator::EffectStrength; + using IVibrator = ::aidl::android::hardware::vibrator::IVibrator; + using Status = ::ndk::ScopedAStatus; + using VibratorOp = std::function<Status(IVibrator*)>; + + VibratorController() : VibratorController(std::make_shared<VibratorProvider>()) {} + VibratorController(std::shared_ptr<VibratorProvider> vibratorProvider) + : mVibratorProvider(std::move(vibratorProvider)), mVibrator(nullptr) {} + virtual ~VibratorController() = default; + + /* Connects HAL service, possibly waiting for the declared service to become available. + * This will automatically be called at the first API usage if it was not manually called + * beforehand. Call this manually during the setup phase to avoid slowing the first API call. + * Returns true if HAL service is declared, false otherwise. + */ + bool init(); + + /* Turn vibrator off. */ + Status off(); + + /* Set vibration amplitude in [0,1]. */ + Status setAmplitude(float amplitude); + + /* Enable/disable external control. */ + Status setExternalControl(bool enabled); + + /* Enable always-on for given id, with given effect and strength. */ + Status alwaysOnEnable(int32_t id, const Effect& effect, const EffectStrength& strength); + + /* Disable always-on for given id. */ + Status alwaysOnDisable(int32_t id); + +private: + /* Max number of attempts to perform an operation when it fails with transaction error. */ + static constexpr int MAX_ATTEMPTS = 2; + + std::mutex mMutex; + std::shared_ptr<VibratorProvider> mVibratorProvider; + std::shared_ptr<IVibrator> mVibrator GUARDED_BY(mMutex); + + /* Reconnects HAL service without waiting for the service to become available. */ + std::shared_ptr<IVibrator> reconnectToVibrator(); + + /* Perform given operation on HAL with retries on transaction failures. */ + Status doWithRetries(const VibratorOp& op, const char* logLabel); + + /* Perform given operation on HAL with logs for error/unsupported results. */ + static Status doOnce(IVibrator* vibrator, const VibratorOp& op, const char* logLabel); +}; + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android + +#endif // ANDROID_OS_VIBRATOR_CONTROLLER_H diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h index a1cb3fad35..9b3202bc60 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h @@ -14,6 +14,8 @@ * limitations under the License. */ +// TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed + #ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H #define ANDROID_OS_VIBRATORHALCONTROLLER_H diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h index 9a39ad4f7b..68568d4163 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -14,6 +14,8 @@ * limitations under the License. */ +// TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed + #ifndef ANDROID_OS_VIBRATORHALWRAPPER_H #define ANDROID_OS_VIBRATORHALWRAPPER_H @@ -22,7 +24,6 @@ #include <android-base/thread_annotations.h> #include <android/binder_manager.h> -#include <android/hardware/vibrator/1.3/IVibrator.h> #include <binder/IServiceManager.h> #include <vibratorservice/VibratorCallbackScheduler.h> @@ -105,26 +106,6 @@ public: : fromFailedStatus<T>(std::move(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(std::move(data)) - : fromFailedStatus<T>(std::move(status)); - } - - template <typename T, typename R> - static HalResult<T> fromReturn(hardware::Return<R>&& ret, T data) { - return ret.isOk() ? HalResult<T>::ok(std::move(data)) - : fromFailedReturn<T, R>(std::move(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>(std::move(status), std::move(data)) - : fromFailedReturn<T, R>(std::move(ret)); - } - static HalResult<void> fromStatus(status_t status) { return (status == android::OK) ? HalResult<void>::ok() : fromFailedStatus<void>(std::move(status)); @@ -134,17 +115,6 @@ public: return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(std::move(status)); } - static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status&& status) { - return (status == hardware::vibrator::V1_0::Status::OK) - ? HalResult<void>::ok() - : fromFailedStatus<void>(std::move(status)); - } - - template <typename R> - static HalResult<void> fromReturn(hardware::Return<R>&& ret) { - return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(std::move(ret)); - } - private: template <typename T> static HalResult<T> fromFailedStatus(status_t status) { @@ -166,23 +136,6 @@ private: } return HalResult<T>::failed(status.getMessage()); } - - 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()); - } - } - - 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()); - } }; // ------------------------------------------------------------------------------------------------- @@ -548,108 +501,6 @@ private: std::shared_ptr<IVibrator> getHal(); }; -// Wrapper for the HDIL Vibrator HALs. -template <typename I> -class HidlHalWrapper : public HalWrapper { -public: - HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle) - : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {} - virtual ~HidlHalWrapper() = default; - - HalResult<void> ping() override final; - void tryReconnect() override final; - - HalResult<void> on(std::chrono::milliseconds timeout, - const std::function<void()>& completionCallback) override final; - HalResult<void> off() override final; - - HalResult<void> setAmplitude(float amplitude) override final; - virtual HalResult<void> setExternalControl(bool enabled) override; - - HalResult<void> alwaysOnEnable(int32_t id, HalWrapper::Effect effect, - HalWrapper::EffectStrength strength) override final; - HalResult<void> alwaysOnDisable(int32_t id) override final; - -protected: - std::mutex mHandleMutex; - sp<I> mHandle GUARDED_BY(mHandleMutex); - - virtual HalResult<Capabilities> getCapabilitiesInternal() override; - - template <class T> - using perform_fn = - hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength, - hardware::vibrator::V1_0::IVibrator::perform_cb); - - template <class T> - HalResult<std::chrono::milliseconds> performInternal( - perform_fn<T> performFn, sp<I> handle, T effect, HalWrapper::EffectStrength strength, - const std::function<void()>& completionCallback); - - sp<I> getHal(); -}; - -// Wrapper for the HDIL Vibrator HAL v1.0. -class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> { -public: - HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler, - sp<hardware::vibrator::V1_0::IVibrator> handle) - : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler), - std::move(handle)) {} - virtual ~HidlHalWrapperV1_0() = default; - - HalResult<std::chrono::milliseconds> performEffect( - HalWrapper::Effect effect, HalWrapper::EffectStrength strength, - const std::function<void()>& completionCallback) override final; -}; - -// Wrapper for the HDIL Vibrator HAL v1.1. -class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> { -public: - HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler, - sp<hardware::vibrator::V1_1::IVibrator> handle) - : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler), - std::move(handle)) {} - virtual ~HidlHalWrapperV1_1() = default; - - HalResult<std::chrono::milliseconds> performEffect( - HalWrapper::Effect effect, HalWrapper::EffectStrength strength, - const std::function<void()>& completionCallback) override final; -}; - -// Wrapper for the HDIL Vibrator HAL v1.2. -class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> { -public: - HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler, - sp<hardware::vibrator::V1_2::IVibrator> handle) - : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler), - std::move(handle)) {} - virtual ~HidlHalWrapperV1_2() = default; - - HalResult<std::chrono::milliseconds> performEffect( - HalWrapper::Effect effect, HalWrapper::EffectStrength strength, - const std::function<void()>& completionCallback) override final; -}; - -// Wrapper for the HDIL Vibrator HAL v1.3. -class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> { -public: - HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler, - sp<hardware::vibrator::V1_3::IVibrator> handle) - : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler), - std::move(handle)) {} - virtual ~HidlHalWrapperV1_3() = default; - - HalResult<void> setExternalControl(bool enabled) override final; - - HalResult<std::chrono::milliseconds> performEffect( - HalWrapper::Effect effect, HalWrapper::EffectStrength strength, - const std::function<void()>& completionCallback) override final; - -protected: - HalResult<Capabilities> getCapabilitiesInternal() override final; -}; - // ------------------------------------------------------------------------------------------------- }; // namespace vibrator diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp index 92527eb5cf..92c6286513 100644 --- a/services/vibratorservice/test/Android.bp +++ b/services/vibratorservice/test/Android.bp @@ -27,12 +27,9 @@ cc_test { test_suites: ["device-tests"], srcs: [ "VibratorCallbackSchedulerTest.cpp", + "VibratorControllerTest.cpp", "VibratorHalControllerTest.cpp", "VibratorHalWrapperAidlTest.cpp", - "VibratorHalWrapperHidlV1_0Test.cpp", - "VibratorHalWrapperHidlV1_1Test.cpp", - "VibratorHalWrapperHidlV1_2Test.cpp", - "VibratorHalWrapperHidlV1_3Test.cpp", "VibratorManagerHalControllerTest.cpp", "VibratorManagerHalWrapperAidlTest.cpp", "VibratorManagerHalWrapperLegacyTest.cpp", @@ -43,17 +40,12 @@ cc_test { "-Wextra", ], shared_libs: [ + "android.hardware.vibrator-V3-ndk", "libbase", "libbinder_ndk", - "libhidlbase", "liblog", - "libvibratorservice", "libutils", - "android.hardware.vibrator-V3-ndk", - "android.hardware.vibrator@1.0", - "android.hardware.vibrator@1.1", - "android.hardware.vibrator@1.2", - "android.hardware.vibrator@1.3", + "libvibratorservice", ], static_libs: [ "libgmock", diff --git a/services/vibratorservice/test/VibratorControllerTest.cpp b/services/vibratorservice/test/VibratorControllerTest.cpp new file mode 100644 index 0000000000..11ec75bc36 --- /dev/null +++ b/services/vibratorservice/test/VibratorControllerTest.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2025 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 "VibratorControllerTest" + +#include <aidl/android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorController.h> + +#include "test_mocks.h" +#include "test_utils.h" + +using ::aidl::android::hardware::vibrator::Effect; +using ::aidl::android::hardware::vibrator::EffectStrength; +using ::aidl::android::hardware::vibrator::IVibrator; + +using namespace android; +using namespace testing; + +const auto kReturnOk = []() { return ndk::ScopedAStatus::ok(); }; +const auto kReturnUnsupported = []() { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +}; +const auto kReturnTransactionFailed = []() { + return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED); +}; +const auto kReturnUnknownTransaction = []() { + return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION); +}; +const auto kReturnIllegalArgument = []() { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +}; + +// ------------------------------------------------------------------------------------------------- + +/* Provides mock IVibrator instance for testing. */ +class FakeVibratorProvider : public vibrator::VibratorProvider { +public: + FakeVibratorProvider() + : mIsDeclared(true), + mMockVibrator(ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>()), + mConnectCount(0), + mReconnectCount(0) {} + virtual ~FakeVibratorProvider() = default; + + bool isDeclared() override { return mIsDeclared; } + + std::shared_ptr<IVibrator> waitForVibrator() override { + mConnectCount++; + return mIsDeclared ? mMockVibrator : nullptr; + } + + std::shared_ptr<IVibrator> checkForVibrator() override { + mReconnectCount++; + return mIsDeclared ? mMockVibrator : nullptr; + } + + void setDeclared(bool isDeclared) { mIsDeclared = isDeclared; } + + int32_t getConnectCount() { return mConnectCount; } + + int32_t getReconnectCount() { return mReconnectCount; } + + std::shared_ptr<StrictMock<vibrator::MockIVibrator>> getMockVibrator() { return mMockVibrator; } + +private: + bool mIsDeclared; + std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator; + int32_t mConnectCount; + int32_t mReconnectCount; +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorControllerTest : public Test { +public: + void SetUp() override { + mProvider = std::make_shared<FakeVibratorProvider>(); + mController = std::make_unique<vibrator::VibratorController>(mProvider); + ASSERT_NE(mController, nullptr); + } + +protected: + std::shared_ptr<FakeVibratorProvider> mProvider = nullptr; + std::unique_ptr<vibrator::VibratorController> mController = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorControllerTest, TestInitServiceDeclared) { + ASSERT_TRUE(mController->init()); + ASSERT_EQ(1, mProvider->getConnectCount()); + ASSERT_EQ(0, mProvider->getReconnectCount()); + + // Noop when wrapper was already initialized. + ASSERT_TRUE(mController->init()); + ASSERT_EQ(1, mProvider->getConnectCount()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestInitServiceNotDeclared) { + mProvider->setDeclared(false); + + ASSERT_FALSE(mController->init()); + ASSERT_EQ(0, mProvider->getConnectCount()); + ASSERT_EQ(0, mProvider->getReconnectCount()); + + ASSERT_FALSE(mController->init()); + ASSERT_EQ(0, mProvider->getConnectCount()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestFirstCallTriggersInit) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + + auto status = mController->off(); + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(1, mProvider->getConnectCount()); +} + +TEST_F(VibratorControllerTest, TestSuccessfulResultDoesNotRetry) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + + auto status = mController->off(); + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestUnsupportedOperationResultDoesNotRetry) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnUnsupported); + + auto status = mController->off(); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestUnknownTransactionResultDoesNotRetry) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnUnknownTransaction); + + auto status = mController->off(); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestOperationFailedDoesNotRetry) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnIllegalArgument); + + auto status = mController->off(); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestTransactionFailedRetriesOnlyOnce) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(2)) + .WillRepeatedly(kReturnTransactionFailed); + + auto status = mController->off(); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(1, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestTransactionFailedThenSucceedsReturnsSuccessAfterRetries) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(2)) + .WillOnce(kReturnTransactionFailed) + .WillRepeatedly(kReturnOk); + + auto status = mController->off(); + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(1, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestOff) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + + auto status = mController->off(); + ASSERT_TRUE(status.isOk()); +} + +TEST_F(VibratorControllerTest, TestSetAmplitude) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), setAmplitude(Eq(0.1f))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + EXPECT_CALL(*mProvider->getMockVibrator().get(), setAmplitude(Eq(0.2f))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnIllegalArgument); + + ASSERT_TRUE(mController->setAmplitude(0.1f).isOk()); + ASSERT_FALSE(mController->setAmplitude(0.2f).isOk()); +} + +TEST_F(VibratorControllerTest, TestSetExternalControl) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), setExternalControl(Eq(false))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + EXPECT_CALL(*mProvider->getMockVibrator().get(), setExternalControl(Eq(true))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnIllegalArgument); + + ASSERT_TRUE(mController->setExternalControl(false).isOk()); + ASSERT_FALSE(mController->setExternalControl(true).isOk()); +} + +TEST_F(VibratorControllerTest, TestAlwaysOnEnable) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), + alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + EXPECT_CALL(*mProvider->getMockVibrator().get(), + alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnIllegalArgument); + + ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk()); + ASSERT_FALSE(mController->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM).isOk()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp deleted file mode 100644 index 04dbe4eb26..0000000000 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "VibratorHalWrapperHidlV1_0Test" - -#include <aidl/android/hardware/vibrator/IVibrator.h> -#include <android/persistable_bundle_aidl.h> - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <utils/Log.h> -#include <thread> - -#include <vibratorservice/VibratorCallbackScheduler.h> -#include <vibratorservice/VibratorHalWrapper.h> - -#include "test_mocks.h" -#include "test_utils.h" - -namespace V1_0 = android::hardware::vibrator::V1_0; - -using aidl::android::hardware::vibrator::Braking; -using aidl::android::hardware::vibrator::CompositeEffect; -using aidl::android::hardware::vibrator::CompositePrimitive; -using aidl::android::hardware::vibrator::CompositePwleV2; -using aidl::android::hardware::vibrator::Effect; -using aidl::android::hardware::vibrator::EffectStrength; -using aidl::android::hardware::vibrator::IVibrator; -using aidl::android::hardware::vibrator::PrimitivePwle; -using aidl::android::hardware::vibrator::PwleV2Primitive; -using aidl::android::hardware::vibrator::VendorEffect; -using aidl::android::os::PersistableBundle; - -using namespace android; -using namespace std::chrono_literals; -using namespace testing; - -// ------------------------------------------------------------------------------------------------- - -class MockIVibratorV1_0 : public V1_0::IVibrator { -public: - MOCK_METHOD(hardware::Return<void>, ping, (), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); - MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); - MOCK_METHOD(hardware::Return<void>, perform, - (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); -}; - -// ------------------------------------------------------------------------------------------------- - -class VibratorHalWrapperHidlV1_0Test : public Test { -public: - void SetUp() override { - mMockHal = new StrictMock<MockIVibratorV1_0>(); - mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); - mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal); - ASSERT_NE(mWrapper, nullptr); - } - -protected: - std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; - std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; - sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr; -}; - -// ------------------------------------------------------------------------------------------------- - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) { - EXPECT_CALL(*mMockHal.get(), ping()) - .Times(Exactly(2)) - .WillOnce([]() { return hardware::Return<void>(); }) - .WillRepeatedly([]() { - return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); - }); - - ASSERT_TRUE(mWrapper->ping().isOk()); - ASSERT_TRUE(mWrapper->ping().isFailed()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1)))) - .Times(Exactly(1)) - .WillRepeatedly( - [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10)))) - .Times(Exactly(1)) - .WillRepeatedly([](uint32_t) { - return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); - }); - EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11)))) - .Times(Exactly(1)) - .WillRepeatedly([](uint32_t) { - return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); - }); - EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12)))) - .Times(Exactly(1)) - .WillRepeatedly([](uint32_t) { - return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); - }); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - ASSERT_TRUE(mWrapper->on(1ms, callback).isOk()); - ASSERT_EQ(1, *callbackCounter.get()); - - ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported()); - ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed()); - ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed()); - - // Callback not triggered for unsupported and on failure - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) { - EXPECT_CALL(*mMockHal.get(), off()) - .Times(Exactly(4)) - .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }) - .WillOnce([]() { - return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); - }) - .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); }) - .WillRepeatedly([]() { - return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); - }); - - ASSERT_TRUE(mWrapper->off().isOk()); - ASSERT_TRUE(mWrapper->off().isUnsupported()); - ASSERT_TRUE(mWrapper->off().isFailed()); - ASSERT_TRUE(mWrapper->off().isFailed()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(1)))) - .Times(Exactly(1)) - .WillRepeatedly( - [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }); - EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2)))) - .Times(Exactly(1)) - .WillRepeatedly([](uint8_t) { - return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); - }); - EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3)))) - .Times(Exactly(1)) - .WillRepeatedly([](uint8_t) { - return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); - }); - EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4)))) - .Times(Exactly(1)) - .WillRepeatedly([](uint8_t) { - return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); - }); - } - - auto maxAmplitude = std::numeric_limits<uint8_t>::max(); - ASSERT_TRUE(mWrapper->setAmplitude(1.0f / maxAmplitude).isOk()); - ASSERT_TRUE(mWrapper->setAmplitude(2.0f / maxAmplitude).isUnsupported()); - ASSERT_TRUE(mWrapper->setAmplitude(3.0f / maxAmplitude).isFailed()); - ASSERT_TRUE(mWrapper->setAmplitude(4.0f / maxAmplitude).isFailed()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) { - ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported()); - ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) { - ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) { - ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) { - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(2)) - .WillOnce([]() { - return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); - }) - .WillRepeatedly([]() { return hardware::Return<bool>(true); }); - - ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); - - vibrator::Info info = mWrapper->getInfo(); - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value()); - ASSERT_TRUE(info.supportedEffects.isUnsupported()); - ASSERT_TRUE(info.supportedBraking.isUnsupported()); - ASSERT_TRUE(info.supportedPrimitives.isUnsupported()); - ASSERT_TRUE(info.primitiveDurations.isUnsupported()); - ASSERT_TRUE(info.primitiveDelayMax.isUnsupported()); - ASSERT_TRUE(info.pwlePrimitiveDurationMax.isUnsupported()); - ASSERT_TRUE(info.compositionSizeMax.isUnsupported()); - ASSERT_TRUE(info.pwleSizeMax.isUnsupported()); - ASSERT_TRUE(info.minFrequency.isUnsupported()); - ASSERT_TRUE(info.resonantFrequency.isUnsupported()); - ASSERT_TRUE(info.frequencyResolution.isUnsupported()); - ASSERT_TRUE(info.qFactor.isUnsupported()); - ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); - ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported()); - ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported()); - ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported()); - ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) { - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() { - return hardware::Return<bool>(false); - }); - - ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) { - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() { - return hardware::Return<bool>(true); - }); - - std::vector<std::thread> threads; - for (int i = 0; i < 10; i++) { - threads.push_back( - std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); })); - } - std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); - - vibrator::Info info = mWrapper->getInfo(); - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value()); - ASSERT_TRUE(info.supportedEffects.isUnsupported()); - ASSERT_TRUE(info.supportedBraking.isUnsupported()); - ASSERT_TRUE(info.supportedPrimitives.isUnsupported()); - ASSERT_TRUE(info.primitiveDurations.isUnsupported()); - ASSERT_TRUE(info.minFrequency.isUnsupported()); - ASSERT_TRUE(info.resonantFrequency.isUnsupported()); - ASSERT_TRUE(info.frequencyResolution.isUnsupported()); - ASSERT_TRUE(info.qFactor.isUnsupported()); - ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); - ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported()); - ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported()); - ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported()); - ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - EXPECT_CALL(*mMockHal.get(), - perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { - cb(V1_0::Status::UNSUPPORTED_OPERATION, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockHal.get(), - perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _)) - .Times(Exactly(2)) - .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { - cb(V1_0::Status::BAD_VALUE, 10); - return hardware::Return<void>(); - }) - .WillRepeatedly( - [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) { - return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); - }); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); - - result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback); - ASSERT_TRUE(result.isUnsupported()); - - result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback); - ASSERT_TRUE(result.isFailed()); - - result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback); - ASSERT_TRUE(result.isFailed()); - - // Callback not triggered for unsupported and on failure - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) { - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - // Using TICK that is only available in v1.1 - auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); - ASSERT_TRUE(result.isUnsupported()); - // No callback is triggered. - ASSERT_EQ(0, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformVendorEffectUnsupported) { - PersistableBundle vendorData; // empty - VendorEffect vendorEffect; - vendorEffect.vendorData = vendorData; - vendorEffect.strength = EffectStrength::LIGHT; - vendorEffect.scale = 1.0f; - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - ASSERT_TRUE(mWrapper->performVendorEffect(vendorEffect, callback).isUnsupported()); - - // No callback is triggered. - ASSERT_EQ(0, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) { - std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects; - singleEffect.push_back( - vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f)); - multipleEffects.push_back( - vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f)); - multipleEffects.push_back( - vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)); - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported()); - ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported()); - - // No callback is triggered. - ASSERT_EQ(0, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) { - std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives; - multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms)); - multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms)); - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - ASSERT_TRUE(mWrapper->performPwleEffect(emptyPrimitives, callback).isUnsupported()); - ASSERT_TRUE(mWrapper->performPwleEffect(multiplePrimitives, callback).isUnsupported()); - - // No callback is triggered. - ASSERT_EQ(0, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_0Test, TestComposePwleV2Unsupported) { - CompositePwleV2 composite; - composite.pwlePrimitives = { - PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100), - PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100), - PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100), - }; - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - ASSERT_TRUE(mWrapper->composePwleV2(composite, callback).isUnsupported()); - - // No callback is triggered. - ASSERT_EQ(0, *callbackCounter.get()); -} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp deleted file mode 100644 index b0a653769e..0000000000 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "VibratorHalWrapperHidlV1_1Test" - -#include <aidl/android/hardware/vibrator/IVibrator.h> - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <utils/Log.h> - -#include <vibratorservice/VibratorCallbackScheduler.h> -#include <vibratorservice/VibratorHalWrapper.h> - -#include "test_mocks.h" -#include "test_utils.h" - -namespace V1_0 = android::hardware::vibrator::V1_0; -namespace V1_1 = android::hardware::vibrator::V1_1; - -using aidl::android::hardware::vibrator::Effect; -using aidl::android::hardware::vibrator::EffectStrength; - -using namespace android; -using namespace std::chrono_literals; -using namespace testing; - -// ------------------------------------------------------------------------------------------------- - -class MockIVibratorV1_1 : public V1_1::IVibrator { -public: - MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); - MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); - MOCK_METHOD(hardware::Return<void>, perform, - (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); - MOCK_METHOD(hardware::Return<void>, perform_1_1, - (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), - (override)); -}; - -// ------------------------------------------------------------------------------------------------- - -class VibratorHalWrapperHidlV1_1Test : public Test { -public: - void SetUp() override { - mMockHal = new StrictMock<MockIVibratorV1_1>(); - mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); - mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal); - ASSERT_NE(mWrapper, nullptr); - } - -protected: - std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; - std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; - sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr; -}; - -// ------------------------------------------------------------------------------------------------- - -TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); - - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, - MockIVibratorV1_1::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - EXPECT_CALL(*mMockHal.get(), - perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _)) - .Times(Exactly(1)) - .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, - MockIVibratorV1_1::perform_cb cb) { - cb(V1_0::Status::UNSUPPORTED_OPERATION, 0); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockHal.get(), - perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _)) - .Times(Exactly(2)) - .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength, - MockIVibratorV1_1::perform_cb cb) { - cb(V1_0::Status::BAD_VALUE, 0); - return hardware::Return<void>(); - }) - .WillRepeatedly( - [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) { - return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); - }); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); - - result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback); - ASSERT_TRUE(result.isUnsupported()); - - result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback); - ASSERT_TRUE(result.isFailed()); - - result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback); - ASSERT_TRUE(result.isFailed()); - - // Callback not triggered for unsupported and on failure - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) { - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - // Using THUD that is only available in v1.2 - auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); - ASSERT_TRUE(result.isUnsupported()); - // No callback is triggered. - ASSERT_EQ(0, *callbackCounter.get()); -} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp deleted file mode 100644 index dfe3fa0e68..0000000000 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "VibratorHalWrapperHidlV1_2Test" - -#include <aidl/android/hardware/vibrator/IVibrator.h> - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <utils/Log.h> - -#include <vibratorservice/VibratorCallbackScheduler.h> -#include <vibratorservice/VibratorHalWrapper.h> - -#include "test_mocks.h" -#include "test_utils.h" - -namespace V1_0 = android::hardware::vibrator::V1_0; -namespace V1_1 = android::hardware::vibrator::V1_1; -namespace V1_2 = android::hardware::vibrator::V1_2; - -using aidl::android::hardware::vibrator::Effect; -using aidl::android::hardware::vibrator::EffectStrength; - -using namespace android; -using namespace std::chrono_literals; -using namespace testing; - -// ------------------------------------------------------------------------------------------------- - -class MockIVibratorV1_2 : public V1_2::IVibrator { -public: - MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); - MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); - MOCK_METHOD(hardware::Return<void>, perform, - (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); - MOCK_METHOD(hardware::Return<void>, perform_1_1, - (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), - (override)); - MOCK_METHOD(hardware::Return<void>, perform_1_2, - (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); -}; - -// ------------------------------------------------------------------------------------------------- - -class VibratorHalWrapperHidlV1_2Test : public Test { -public: - void SetUp() override { - mMockHal = new StrictMock<MockIVibratorV1_2>(); - mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); - mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal); - ASSERT_NE(mWrapper, nullptr); - } - -protected: - std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; - std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; - sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr; -}; - -// ------------------------------------------------------------------------------------------------- - -TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); - - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, - MockIVibratorV1_2::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); - - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - EXPECT_CALL(*mMockHal.get(), - perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { - cb(V1_0::Status::UNSUPPORTED_OPERATION, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockHal.get(), - perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _)) - .Times(Exactly(2)) - .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { - cb(V1_0::Status::BAD_VALUE, 10); - return hardware::Return<void>(); - }) - .WillRepeatedly( - [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) { - return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); - }); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); - - result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback); - ASSERT_TRUE(result.isUnsupported()); - - result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); - ASSERT_TRUE(result.isFailed()); - - result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); - ASSERT_TRUE(result.isFailed()); - - // Callback not triggered for unsupported and on failure - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) { - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - // Using TEXTURE_TICK that is only available in v1.3 - auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback); - ASSERT_TRUE(result.isUnsupported()); - // No callback is triggered. - ASSERT_EQ(0, *callbackCounter.get()); -} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp deleted file mode 100644 index 86243326ac..0000000000 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "VibratorHalWrapperHidlV1_3Test" - -#include <aidl/android/hardware/vibrator/IVibrator.h> - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <utils/Log.h> -#include <thread> - -#include <vibratorservice/VibratorCallbackScheduler.h> -#include <vibratorservice/VibratorHalWrapper.h> - -#include "test_mocks.h" -#include "test_utils.h" - -namespace V1_0 = android::hardware::vibrator::V1_0; -namespace V1_1 = android::hardware::vibrator::V1_1; -namespace V1_2 = android::hardware::vibrator::V1_2; -namespace V1_3 = android::hardware::vibrator::V1_3; - -using aidl::android::hardware::vibrator::Effect; -using aidl::android::hardware::vibrator::EffectStrength; -using aidl::android::hardware::vibrator::IVibrator; - -using namespace android; -using namespace std::chrono_literals; -using namespace testing; - -// ------------------------------------------------------------------------------------------------- - -class MockIVibratorV1_3 : public V1_3::IVibrator { -public: - MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); - MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); - MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); - MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override)); - MOCK_METHOD(hardware::Return<void>, perform, - (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); - MOCK_METHOD(hardware::Return<void>, perform_1_1, - (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), - (override)); - MOCK_METHOD(hardware::Return<void>, perform_1_2, - (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); - MOCK_METHOD(hardware::Return<void>, perform_1_3, - (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); -}; - -// ------------------------------------------------------------------------------------------------- - -class VibratorHalWrapperHidlV1_3Test : public Test { -public: - void SetUp() override { - mMockHal = new StrictMock<MockIVibratorV1_3>(); - mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); - mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal); - ASSERT_NE(mWrapper, nullptr); - } - -protected: - std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; - std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; - sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr; -}; - -// ------------------------------------------------------------------------------------------------- - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))) - .Times(Exactly(2)) - .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }) - .WillRepeatedly([]() { - return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); - }); - EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false))) - .Times(Exactly(2)) - .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); }) - .WillRepeatedly([]() { - return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); - }); - } - - ASSERT_TRUE(mWrapper->setExternalControl(true).isOk()); - ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported()); - ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); - ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { return hardware::Return<bool>(true); }); - EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { - return hardware::Return<bool>(true); - }); - } - - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL | - vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, - mWrapper->getInfo().capabilities.value()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() { - return hardware::Return<bool>(true); - }); - EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { - return hardware::Return<bool>(false); - }); - } - - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() { - return hardware::Return<bool>(false); - }); - EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { - return hardware::Return<bool>(true); - }); - } - - ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { return hardware::Return<bool>(false); }); - EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { - return hardware::Return<bool>(false); - }); - } - - ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { - return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); - }); - - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { return hardware::Return<bool>(true); }); - EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { - return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); - }); - } - - ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); - ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { return hardware::Return<bool>(true); }); - EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { - return hardware::Return<bool>(false); - }); - } - - std::vector<std::thread> threads; - for (int i = 0; i < 10; i++) { - threads.push_back( - std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); })); - } - std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); - - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { - return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); - }); - - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { return hardware::Return<bool>(true); }); - EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { - return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); - }); - - EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { return hardware::Return<bool>(true); }); - EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) - .Times(Exactly(1)) - .WillRepeatedly([]() { return hardware::Return<bool>(false); }); - } - - // Call to supportsAmplitudeControl failed. - ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); - - // Call to supportsExternalControl failed. - ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); - - // Returns successful result from third call. - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value()); - - // Returns cached successful result. - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); - - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, - MockIVibratorV1_3::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); - - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); - - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); -} - -TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) { - { - InSequence seq; - EXPECT_CALL(*mMockHal.get(), - perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { - cb(V1_0::Status::OK, 10); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) - .Times(Exactly(1)) - .WillRepeatedly(vibrator::TriggerSchedulerCallback()); - EXPECT_CALL(*mMockHal.get(), - perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM), - _)) - .Times(Exactly(1)) - .WillRepeatedly( - [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { - cb(V1_0::Status::UNSUPPORTED_OPERATION, 0); - return hardware::Return<void>(); - }); - EXPECT_CALL(*mMockHal.get(), - perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG), - _)) - .Times(Exactly(2)) - .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { - cb(V1_0::Status::BAD_VALUE, 0); - return hardware::Return<void>(); - }) - .WillRepeatedly( - [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) { - return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); - }); - } - - std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); - auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - - auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(10ms, result.value()); - ASSERT_EQ(1, *callbackCounter.get()); - - result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback); - ASSERT_TRUE(result.isUnsupported()); - - result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback); - ASSERT_TRUE(result.isFailed()); - - result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback); - ASSERT_TRUE(result.isFailed()); - - // Callback not triggered for unsupported and on failure - ASSERT_EQ(1, *callbackCounter.get()); -} diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 879d2d0fa7..d75058abe3 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -22,6 +22,13 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +// Expose internal header files to test testing binary +cc_library_headers { + name: "libvulkanprivate_headers-testing", + export_include_dirs: ["."], + visibility: ["//frameworks/native/vulkan/tests"], +} + ndk_library { name: "libvulkan", symbol_file: "libvulkan.map.txt", @@ -41,14 +48,9 @@ cc_aconfig_library { aconfig_declarations: "libvulkan_flags", } -cc_library_shared { - name: "libvulkan", - llndk: { - symbol_file: "libvulkan.map.txt", - export_llndk_headers: [ - "vulkan_headers", - ], - }, +cc_defaults { + name: "libvulkan_defaults", + sanitize: { misc_undefined: ["integer"], }, @@ -81,6 +83,34 @@ cc_library_shared { "-Wno-global-constructors", "-Wno-zero-length-array", ], +} + +cc_library { + name: "libvulkanallocator", + defaults: ["libvulkan_defaults"], + cflags: [ + // This code uses malloc_usable_size(), + // and thus can't be built with _FORTIFY_SOURCE=3. + "-U_FORTIFY_SOURCE", + "-D_FORTIFY_SOURCE=2", + ], + srcs: [ + "allocator.cpp", + ], + header_libs: [ + "vulkan_headers", + ], +} + +cc_library_shared { + name: "libvulkan", + defaults: ["libvulkan_defaults"], + llndk: { + symbol_file: "libvulkan.map.txt", + export_llndk_headers: [ + "vulkan_headers", + ], + }, srcs: [ "api.cpp", @@ -124,6 +154,7 @@ cc_library_shared { ], static_libs: [ "libgrallocusage", + "libvulkanallocator", "libvulkanflags", ], } diff --git a/vulkan/libvulkan/TEST_MAPPING b/vulkan/libvulkan/TEST_MAPPING new file mode 100644 index 0000000000..16e342b7c0 --- /dev/null +++ b/vulkan/libvulkan/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libvulkan_test" + } + ] +} diff --git a/vulkan/libvulkan/allocator.cpp b/vulkan/libvulkan/allocator.cpp new file mode 100644 index 0000000000..2ca0586a14 --- /dev/null +++ b/vulkan/libvulkan/allocator.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "allocator.h" + +#include <stdlib.h> + +#include <algorithm> + +#include <log/log.h> + +// #define ENABLE_ALLOC_CALLSTACKS 1 +#if ENABLE_ALLOC_CALLSTACKS +#include <utils/CallStack.h> +#define ALOGD_CALLSTACK(...) \ + do { \ + ALOGD(__VA_ARGS__); \ + android::CallStack callstack; \ + callstack.update(); \ + callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, " "); \ + } while (false) +#else +#define ALOGD_CALLSTACK(...) \ + do { \ + } while (false) +#endif + +namespace vulkan { +namespace driver { + +namespace { + +VKAPI_ATTR void* DefaultAllocate(void*, + size_t size, + size_t alignment, + VkSystemAllocationScope) { + void* ptr = nullptr; + // Vulkan requires 'alignment' to be a power of two, but posix_memalign + // additionally requires that it be at least sizeof(void*). + int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size); + ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment, + ret, ptr); + return ret == 0 ? ptr : nullptr; +} + +// This function is marked `noinline` so that LLVM can't infer an object size +// for FORTIFY through it, given that it's abusing malloc_usable_size(). +__attribute__((__noinline__)) +VKAPI_ATTR void* DefaultReallocate(void*, + void* ptr, + size_t size, + size_t alignment, + VkSystemAllocationScope) { + if (size == 0) { + free(ptr); + return nullptr; + } + + // TODO(b/143295633): Right now we never shrink allocations; if the new + // request is smaller than the existing chunk, we just continue using it. + // Right now the loader never reallocs, so this doesn't matter. If that + // changes, or if this code is copied into some other project, this should + // probably have a heuristic to allocate-copy-free when doing so will save + // "enough" space. + size_t old_size = ptr ? malloc_usable_size(ptr) : 0; + if (size <= old_size) + return ptr; + + void* new_ptr = nullptr; + if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0) + return nullptr; + if (ptr) { + memcpy(new_ptr, ptr, std::min(old_size, size)); + free(ptr); + } + return new_ptr; +} + +VKAPI_ATTR void DefaultFree(void*, void* ptr) { + ALOGD_CALLSTACK("Free: %p", ptr); + free(ptr); +} + +} // anonymous namespace + +const VkAllocationCallbacks& GetDefaultAllocator() { + static const VkAllocationCallbacks kDefaultAllocCallbacks = { + .pUserData = nullptr, + .pfnAllocation = DefaultAllocate, + .pfnReallocation = DefaultReallocate, + .pfnFree = DefaultFree, + }; + + return kDefaultAllocCallbacks; +} + +} // namespace driver +} // namespace vulkan diff --git a/vulkan/libvulkan/allocator.h b/vulkan/libvulkan/allocator.h new file mode 100644 index 0000000000..9095e921c5 --- /dev/null +++ b/vulkan/libvulkan/allocator.h @@ -0,0 +1,25 @@ +/* + * Copyright 2025 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 <vulkan/vulkan.h> + +namespace vulkan { +namespace driver { + +const VkAllocationCallbacks& GetDefaultAllocator(); + +} // namespace driver +} // namespace vulkan diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 7d0f545774..28c1b5f663 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -50,22 +50,6 @@ using namespace com::android::graphics::libvulkan; extern "C" android_namespace_t* android_get_exported_namespace(const char*); -// #define ENABLE_ALLOC_CALLSTACKS 1 -#if ENABLE_ALLOC_CALLSTACKS -#include <utils/CallStack.h> -#define ALOGD_CALLSTACK(...) \ - do { \ - ALOGD(__VA_ARGS__); \ - android::CallStack callstack; \ - callstack.update(); \ - callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, " "); \ - } while (false) -#else -#define ALOGD_CALLSTACK(...) \ - do { \ - } while (false) -#endif - namespace vulkan { namespace driver { @@ -829,54 +813,6 @@ void CreateInfoWrapper::FilterExtension(const char* name) { } } -VKAPI_ATTR void* DefaultAllocate(void*, - size_t size, - size_t alignment, - VkSystemAllocationScope) { - void* ptr = nullptr; - // Vulkan requires 'alignment' to be a power of two, but posix_memalign - // additionally requires that it be at least sizeof(void*). - int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size); - ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment, - ret, ptr); - return ret == 0 ? ptr : nullptr; -} - -VKAPI_ATTR void* DefaultReallocate(void*, - void* ptr, - size_t size, - size_t alignment, - VkSystemAllocationScope) { - if (size == 0) { - free(ptr); - return nullptr; - } - - // TODO(b/143295633): Right now we never shrink allocations; if the new - // request is smaller than the existing chunk, we just continue using it. - // Right now the loader never reallocs, so this doesn't matter. If that - // changes, or if this code is copied into some other project, this should - // probably have a heuristic to allocate-copy-free when doing so will save - // "enough" space. - size_t old_size = ptr ? malloc_usable_size(ptr) : 0; - if (size <= old_size) - return ptr; - - void* new_ptr = nullptr; - if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0) - return nullptr; - if (ptr) { - memcpy(new_ptr, ptr, std::min(old_size, size)); - free(ptr); - } - return new_ptr; -} - -VKAPI_ATTR void DefaultFree(void*, void* ptr) { - ALOGD_CALLSTACK("Free: %p", ptr); - free(ptr); -} - InstanceData* AllocateInstanceData(const VkAllocationCallbacks& allocator) { void* data_mem = allocator.pfnAllocation( allocator.pUserData, sizeof(InstanceData), alignof(InstanceData), @@ -916,17 +852,6 @@ bool OpenHAL() { return Hal::Open(); } -const VkAllocationCallbacks& GetDefaultAllocator() { - static const VkAllocationCallbacks kDefaultAllocCallbacks = { - .pUserData = nullptr, - .pfnAllocation = DefaultAllocate, - .pfnReallocation = DefaultReallocate, - .pfnFree = DefaultFree, - }; - - return kDefaultAllocCallbacks; -} - PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName) { const ProcHook* hook = GetProcHook(pName); if (!hook) diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h index 4b855e5999..fa85dd5b55 100644 --- a/vulkan/libvulkan/driver.h +++ b/vulkan/libvulkan/driver.h @@ -27,6 +27,7 @@ #include <vulkan/vulkan.h> #include <hardware/hwvulkan.h> +#include "allocator.h" #include "api_gen.h" #include "driver_gen.h" #include "debug_report.h" @@ -102,7 +103,6 @@ struct DeviceData { }; bool OpenHAL(); -const VkAllocationCallbacks& GetDefaultAllocator(); void QueryPresentationProperties( VkPhysicalDevice physicalDevice, diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 09b0a145af..5e2b55ef75 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1413,115 +1413,138 @@ static void DestroySwapchainInternal(VkDevice device, allocator->pfnFree(allocator->pUserData, swapchain); } -static VkResult getProducerUsage(const VkDevice& device, - const VkSwapchainCreateInfoKHR* create_info, - const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage, - bool create_protected_swapchain, - uint64_t* producer_usage) { - // Get the physical device to query the appropriate producer usage - 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) { - // 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; - } +static VkResult getProducerUsageGPDIFP2( + const VkPhysicalDevice& pdev, + const VkSwapchainCreateInfoKHR* create_info, + const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage, + bool create_protected_swapchain, + uint64_t* producer_usage) { + // 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; } + } - // 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, - }; + // 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, + }; - // 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; - } + // 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; + } - 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, - }; + 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, + }; - // If supporting mutable format swapchain add the mutable format flag - if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { - image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; - image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR; - } + // If supporting mutable format swapchain add the mutable format flag + if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { + image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR; + } - VkAndroidHardwareBufferUsageANDROID ahb_usage; - ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID; - ahb_usage.pNext = nullptr; + 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; + VkImageFormatProperties2 image_format_properties; + image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; + image_format_properties.pNext = &ahb_usage; - VkResult result = GetPhysicalDeviceImageFormatProperties2( - pdev, &image_format_info, &image_format_properties); - if (result != VK_SUCCESS) { - ALOGE( - "VkGetPhysicalDeviceImageFormatProperties2 for AHB usage " - "failed: %d", - result); - return VK_ERROR_SURFACE_LOST_KHR; - } + VkResult result = GetPhysicalDeviceImageFormatProperties2( + pdev, &image_format_info, &image_format_properties); + if (result != VK_SUCCESS) { + ALOGE( + "VkGetPhysicalDeviceImageFormatProperties2 for AHB usage " + "failed: %d", + result); + return VK_ERROR_SURFACE_LOST_KHR; + } + // 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; + } - // 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; - } + // 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() + }; - // 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() - }; + // If FRONT_BUFFER is not supported in the GPDIFP2 path + // then we need to fallback to GetSwapchainGrallocUsageXAndroid + if (AHardwareBuffer_isSupported(&ahb_desc)) { + *producer_usage = ahb_usage.androidHardwareBufferUsage; + *producer_usage |= AHARDWAREBUFFER_USAGE_FRONT_BUFFER; + return VK_SUCCESS; + } + + return VK_ERROR_FORMAT_NOT_SUPPORTED; +} + +static VkResult getProducerUsage(const VkDevice& device, + const VkSwapchainCreateInfoKHR* create_info, + const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage, + bool create_protected_swapchain, + uint64_t* producer_usage) { + // Get the physical device to query the appropriate producer usage + const VkPhysicalDevice& pdev = GetData(device).driver_physical_device; + const InstanceData& instance_data = GetData(pdev); + const InstanceDriverTable& instance_dispatch = instance_data.driver; - // 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; + if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2 || + instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) { + VkResult result = + getProducerUsageGPDIFP2(pdev, create_info, swapchain_image_usage, + create_protected_swapchain, producer_usage); + if (result == VK_SUCCESS) { return VK_SUCCESS; } + // Fall through to gralloc path on error } uint64_t native_usage = 0; diff --git a/vulkan/scripts/code_generator.py b/vulkan/scripts/code_generator.py index 2a017d2259..051816de87 100755 --- a/vulkan/scripts/code_generator.py +++ b/vulkan/scripts/code_generator.py @@ -21,6 +21,7 @@ import api_generator import driver_generator import generator_common import null_generator +import vkjson_generator if __name__ == '__main__': generator_common.parse_vulkan_registry() @@ -30,3 +31,6 @@ if __name__ == '__main__': driver_generator.gen_cpp() null_generator.gen_h() null_generator.gen_cpp() + vkjson_generator.gen_h() + vkjson_generator.gen_cc() + vkjson_generator.gen_instance_cc() diff --git a/vulkan/scripts/vk.py b/vulkan/scripts/vk.py new file mode 100644 index 0000000000..983e0dc296 --- /dev/null +++ b/vulkan/scripts/vk.py @@ -0,0 +1,968 @@ +import ctypes +import dataclasses +import enum +from typing import List + +dataclass = dataclasses.dataclass +Enum = enum.Enum + +# TODO(b/401184058): Automate this file for generating the vulkan structs graph from vk.xml +VK_UUID_SIZE = 16 +VK_LUID_SIZE = 16 + +VkImageLayout = Enum +uint8_t = ctypes.c_uint8 +uint32_t = ctypes.c_uint32 +VkFlags = uint32_t +VkMemoryPropertyFlags = VkFlags +VkMemoryHeapFlags = VkFlags +int32_t = int +uint64_t = ctypes.c_uint64 +VkBool32 = bool +VkDeviceSize = ctypes.c_uint64 +size_t = int +VkSampleCountFlags = ctypes.c_uint32 +VkFormatFeatureFlags = ctypes.c_uint32 +VkQueueFlags = ctypes.c_uint32 +VkShaderStageFlags = ctypes.c_uint32 +VkSubgroupFeatureFlags = ctypes.c_uint32 +VkResolveModeFlags = ctypes.c_uint32 +float_t = ctypes.c_float +VkShaderFloatControlsIndependence = Enum +VkPointClippingBehavior = Enum +VkPhysicalDeviceType = Enum +VkDriverId = Enum +VkPipelineRobustnessBufferBehavior = Enum + + +@dataclass +class ConformanceVersion: + major: uint8_t + minor: uint8_t + subminor: uint8_t + patch: uint8_t + + +@dataclass +class VkExtent3D: + width: uint32_t + height: uint32_t + depth: uint32_t + + +@dataclass +class VkPhysicalDeviceLimits: + maxImageDimension1D: uint32_t + maxImageDimension2D: uint32_t + maxImageDimension3D: uint32_t + maxImageDimensionCube: uint32_t + maxImageArrayLayers: uint32_t + maxTexelBufferElements: uint32_t + maxUniformBufferRange: uint32_t + maxStorageBufferRange: uint32_t + maxPushConstantsSize: uint32_t + maxMemoryAllocationCount: uint32_t + maxSamplerAllocationCount: uint32_t + bufferImageGranularity: VkDeviceSize + sparseAddressSpaceSize: VkDeviceSize + maxBoundDescriptorSets: uint32_t + maxPerStageDescriptorSamplers: uint32_t + maxPerStageDescriptorUniformBuffers: uint32_t + maxPerStageDescriptorStorageBuffers: uint32_t + maxPerStageDescriptorSampledImages: uint32_t + maxPerStageDescriptorStorageImages: uint32_t + maxPerStageDescriptorInputAttachments: uint32_t + maxPerStageResources: uint32_t + maxDescriptorSetSamplers: uint32_t + maxDescriptorSetUniformBuffers: uint32_t + maxDescriptorSetUniformBuffersDynamic: uint32_t + maxDescriptorSetStorageBuffers: uint32_t + maxDescriptorSetStorageBuffersDynamic: uint32_t + maxDescriptorSetSampledImages: uint32_t + maxDescriptorSetStorageImages: uint32_t + maxDescriptorSetInputAttachments: uint32_t + maxVertexInputAttributes: uint32_t + maxVertexInputBindings: uint32_t + maxVertexInputAttributeOffset: uint32_t + maxVertexInputBindingStride: uint32_t + maxVertexOutputComponents: uint32_t + maxTessellationGenerationLevel: uint32_t + maxTessellationPatchSize: uint32_t + maxTessellationControlPerVertexInputComponents: uint32_t + maxTessellationControlPerVertexOutputComponents: uint32_t + maxTessellationControlPerPatchOutputComponents: uint32_t + maxTessellationControlTotalOutputComponents: uint32_t + maxTessellationEvaluationInputComponents: uint32_t + maxTessellationEvaluationOutputComponents: uint32_t + maxGeometryShaderInvocations: uint32_t + maxGeometryInputComponents: uint32_t + maxGeometryOutputComponents: uint32_t + maxGeometryOutputVertices: uint32_t + maxGeometryTotalOutputComponents: uint32_t + maxFragmentInputComponents: uint32_t + maxFragmentOutputAttachments: uint32_t + maxFragmentDualSrcAttachments: uint32_t + maxFragmentCombinedOutputResources: uint32_t + maxComputeSharedMemorySize: uint32_t + maxComputeWorkGroupCount: uint32_t*3 + maxComputeWorkGroupInvocations: uint32_t + maxComputeWorkGroupSize: uint32_t*3 + subPixelPrecisionBits: uint32_t + subTexelPrecisionBits: uint32_t + mipmapPrecisionBits: uint32_t + maxDrawIndexedIndexValue: uint32_t + maxDrawIndirectCount: uint32_t + maxSamplerLodBias: float + maxSamplerAnisotropy: float + maxViewports: uint32_t + maxViewportDimensions: uint32_t*2 + viewportBoundsRange: float_t*2 + viewportSubPixelBits: uint32_t + minMemoryMapAlignment: size_t + minTexelBufferOffsetAlignment: VkDeviceSize + minUniformBufferOffsetAlignment: VkDeviceSize + minStorageBufferOffsetAlignment: VkDeviceSize + minTexelOffset: int32_t + maxTexelOffset: uint32_t + minTexelGatherOffset: int32_t + maxTexelGatherOffset: uint32_t + minInterpolationOffset: float + maxInterpolationOffset: float + subPixelInterpolationOffsetBits: uint32_t + maxFramebufferWidth: uint32_t + maxFramebufferHeight: uint32_t + maxFramebufferLayers: uint32_t + framebufferColorSampleCounts: VkSampleCountFlags + framebufferDepthSampleCounts: VkSampleCountFlags + framebufferStencilSampleCounts: VkSampleCountFlags + framebufferNoAttachmentsSampleCounts: VkSampleCountFlags + maxColorAttachments: uint32_t + sampledImageColorSampleCounts: VkSampleCountFlags + sampledImageIntegerSampleCounts: VkSampleCountFlags + sampledImageDepthSampleCounts: VkSampleCountFlags + sampledImageStencilSampleCounts: VkSampleCountFlags + storageImageSampleCounts: VkSampleCountFlags + maxSampleMaskWords: uint32_t + timestampComputeAndGraphics: VkBool32 + timestampPeriod: float + maxClipDistances: uint32_t + maxCullDistances: uint32_t + maxCombinedClipAndCullDistances: uint32_t + discreteQueuePriorities: uint32_t + pointSizeRange: float_t*2 + lineWidthRange: float_t*2 + pointSizeGranularity: float + lineWidthGranularity: float + strictLines: VkBool32 + standardSampleLocations: VkBool32 + optimalBufferCopyOffsetAlignment: VkDeviceSize + optimalBufferCopyRowPitchAlignment: VkDeviceSize + nonCoherentAtomSize: VkDeviceSize + + +@dataclass +class VkPhysicalDeviceShaderDrawParameterFeatures: + shaderDrawParameters: VkBool32 + + +@dataclass +class VkExtensionProperties: + extensionName: str + specVersion: uint32_t + + +@dataclass +class VkFormatProperties: + linearTilingFeatures: VkFormatFeatureFlags + optimalTilingFeatures: VkFormatFeatureFlags + bufferFeatures: VkFormatFeatureFlags + + +@dataclass +class VkLayerProperties: + layerName: str + specVersion: uint32_t + implementationVersion: uint32_t + description: str + + +@dataclass +class VkQueueFamilyProperties: + queueFlags: VkQueueFlags + queueCount: uint32_t + timestampValidBits: uint32_t + minImageTransferGranularity: VkExtent3D + + +@dataclass +class VkPhysicalDeviceSparseProperties: + residencyStandard2DBlockShape: VkBool32 + residencyStandard2DMultisampleBlockShape: VkBool32 + residencyStandard3DBlockShape: VkBool32 + residencyAlignedMipSize: VkBool32 + residencyNonResidentStrict: VkBool32 + + +@dataclass +class VkImageFormatProperties: + maxExtent: VkExtent3D + maxMipLevels: uint32_t + maxArrayLayers: uint32_t + sampleCounts: VkSampleCountFlags + maxResourceSize: VkDeviceSize + + +@dataclass +class VkPhysicalDeviceSamplerYcbcrConversionFeatures: + samplerYcbcrConversion: VkBool32 + + +@dataclass +class VkPhysicalDeviceIDProperties: + deviceUUID: uint8_t*VK_UUID_SIZE + driverUUID: uint8_t*VK_UUID_SIZE + deviceLUID: uint8_t*VK_LUID_SIZE + deviceNodeMask: uint32_t + deviceLUIDValid: VkBool32 + + +@dataclass +class VkPhysicalDeviceMaintenance3Properties: + maxPerSetDescriptors: uint32_t + maxMemoryAllocationSize: VkDeviceSize + + +@dataclass +class VkPhysicalDevice16BitStorageFeatures: + storageBuffer16BitAccess: VkBool32 + uniformAndStorageBuffer16BitAccess: VkBool32 + storagePushConstant16: VkBool32 + storageInputOutput16: VkBool32 + + +@dataclass +class VkPhysicalDeviceMultiviewFeatures: + multiview: VkBool32 + multiviewGeometryShader: VkBool32 + multiviewTessellationShader: VkBool32 + + +@dataclass +class VkPhysicalDeviceSubgroupProperties: + subgroupSize: uint32_t + supportedStages: VkShaderStageFlags + supportedOperations: VkSubgroupFeatureFlags + quadOperationsInAllStages: VkBool32 + + +@dataclass +class VkPhysicalDevicePointClippingProperties: + pointClippingBehavior: VkPointClippingBehavior + + +@dataclass +class VkPhysicalDeviceMultiviewProperties: + maxMultiviewViewCount: uint32_t + maxMultiviewInstanceIndex: uint32_t + + +@dataclass +class VkMemoryType: + propertyFlags: VkMemoryPropertyFlags + heapIndex: uint32_t + + +@dataclass +class VkMemoryHeap: + size: VkDeviceSize + flags: VkMemoryHeapFlags + + +@dataclass +class VkPhysicalDeviceMemoryProperties: + memoryTypeCount: uint32_t + memoryTypes: List[VkMemoryType] + memoryHeapCount: uint32_t + memoryHeaps: List[VkMemoryHeap] + + +@dataclass +class VkPhysicalDeviceProperties: + apiVersion: uint32_t + driverVersion: uint32_t + vendorID: uint32_t + deviceID: uint32_t + deviceType: VkPhysicalDeviceType + deviceName: str + pipelineCacheUUID: uint8_t + limits: VkPhysicalDeviceLimits + sparseProperties: VkPhysicalDeviceSparseProperties + + +@dataclass +class VkPhysicalDeviceFeatures: + robustBufferAccess: VkBool32 + fullDrawIndexUint32: VkBool32 + imageCubeArray: VkBool32 + independentBlend: VkBool32 + geometryShader: VkBool32 + tessellationShader: VkBool32 + sampleRateShading: VkBool32 + dualSrcBlend: VkBool32 + logicOp: VkBool32 + multiDrawIndirect: VkBool32 + drawIndirectFirstInstance: VkBool32 + depthClamp: VkBool32 + depthBiasClamp: VkBool32 + fillModeNonSolid: VkBool32 + depthBounds: VkBool32 + wideLines: VkBool32 + largePoints: VkBool32 + alphaToOne: VkBool32 + multiViewport: VkBool32 + samplerAnisotropy: VkBool32 + textureCompressionETC2: VkBool32 + textureCompressionASTC_LDR: VkBool32 + textureCompressionBC: VkBool32 + occlusionQueryPrecise: VkBool32 + pipelineStatisticsQuery: VkBool32 + vertexPipelineStoresAndAtomics: VkBool32 + fragmentStoresAndAtomics: VkBool32 + shaderTessellationAndGeometryPointSize: VkBool32 + shaderImageGatherExtended: VkBool32 + shaderStorageImageExtendedFormats: VkBool32 + shaderStorageImageMultisample: VkBool32 + shaderStorageImageReadWithoutFormat: VkBool32 + shaderStorageImageWriteWithoutFormat: VkBool32 + shaderUniformBufferArrayDynamicIndexing: VkBool32 + shaderSampledImageArrayDynamicIndexing: VkBool32 + shaderStorageBufferArrayDynamicIndexing: VkBool32 + shaderStorageImageArrayDynamicIndexing: VkBool32 + shaderClipDistance: VkBool32 + shaderCullDistance: VkBool32 + shaderFloat64: VkBool32 + shaderInt64: VkBool32 + shaderInt16: VkBool32 + shaderResourceResidency: VkBool32 + shaderResourceMinLod: VkBool32 + sparseBinding: VkBool32 + sparseResidencyBuffer: VkBool32 + sparseResidencyImage2D: VkBool32 + sparseResidencyImage3D: VkBool32 + sparseResidency2Samples: VkBool32 + sparseResidency4Samples: VkBool32 + sparseResidency8Samples: VkBool32 + sparseResidency16Samples: VkBool32 + sparseResidencyAliased: VkBool32 + variableMultisampleRate: VkBool32 + inheritedQueries: VkBool32 + + +@dataclass +class VkPhysicalDeviceShaderFloat16Int8Features: + shaderFloat16: VkBool32 + shaderInt8: VkBool32 + + +@dataclass +class VkPhysicalDeviceProtectedMemoryFeatures: + protectedMemory: VkBool32 + + +@dataclass +class VkPhysicalDeviceVariablePointersFeatures: + variablePointersStorageBuffer: VkBool32 + variablePointers: VkBool32 + + +@dataclass +class VkPhysicalDeviceImage2DViewOf3DFeaturesEXT: + image2DViewOf3D: VkBool32 + sampler2DViewOf3D: VkBool32 + + +@dataclass +class VkPhysicalDeviceCustomBorderColorFeaturesEXT: + customBorderColors: VkBool32 + customBorderColorWithoutFormat: VkBool32 + + +@dataclass +class VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT: + primitiveTopologyListRestart: VkBool32 + primitiveTopologyPatchListRestart: VkBool32 + + +@dataclass +class VkPhysicalDeviceProvokingVertexFeaturesEXT: + provokingVertexLast: VkBool32 + transformFeedbackPreservesProvokingVertex: VkBool32 + + +@dataclass +class VkPhysicalDeviceIndexTypeUint8Features: + indexTypeUint8: VkBool32 + + +@dataclass +class VkPhysicalDeviceVertexAttributeDivisorFeatures: + vertexAttributeInstanceRateDivisor: VkBool32 + vertexAttributeInstanceRateZeroDivisor: VkBool32 + + +@dataclass +class VkPhysicalDeviceTransformFeedbackFeaturesEXT: + transformFeedback: VkBool32 + geometryStreams: VkBool32 + + +@dataclass +class VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR: + shaderSubgroupUniformControlFlow: VkBool32 + + +@dataclass +class VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures: + shaderSubgroupExtendedTypes: VkBool32 + + +@dataclass +class VkPhysicalDevice8BitStorageFeatures: + storageBuffer8BitAccess: VkBool32 + uniformAndStorageBuffer8BitAccess: VkBool32 + storagePushConstant8: VkBool32 + + +@dataclass +class VkPhysicalDeviceShaderIntegerDotProductFeatures: + shaderIntegerDotProduct: VkBool32 + + +@dataclass +class VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG: + relaxedLineRasterization: VkBool32 + + +@dataclass +class VkPhysicalDeviceLineRasterizationFeatures: + rectangularLines: VkBool32 + bresenhamLines: VkBool32 + smoothLines: VkBool32 + stippledRectangularLines: VkBool32 + stippledBresenhamLines: VkBool32 + stippledSmoothLines: VkBool32 + + +@dataclass +class VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT: + primitivesGeneratedQuery: VkBool32 + primitivesGeneratedQueryWithRasterizerDiscard: VkBool32 + primitivesGeneratedQueryWithNonZeroStreams: VkBool32 + + +@dataclass +class VkPhysicalDeviceFloatControlsProperties: + denormBehaviorIndependence : VkShaderFloatControlsIndependence + roundingModeIndependence : VkShaderFloatControlsIndependence + shaderSignedZeroInfNanPreserveFloat16 : VkBool32 + shaderSignedZeroInfNanPreserveFloat32 : VkBool32 + shaderSignedZeroInfNanPreserveFloat64 : VkBool32 + shaderDenormPreserveFloat16 : VkBool32 + shaderDenormPreserveFloat32 : VkBool32 + shaderDenormPreserveFloat64 : VkBool32 + shaderDenormFlushToZeroFloat16 : VkBool32 + shaderDenormFlushToZeroFloat32 : VkBool32 + shaderDenormFlushToZeroFloat64 : VkBool32 + shaderRoundingModeRTEFloat16 : VkBool32 + shaderRoundingModeRTEFloat32 : VkBool32 + shaderRoundingModeRTEFloat64 :VkBool32 + shaderRoundingModeRTZFloat16 : VkBool32 + shaderRoundingModeRTZFloat32 : VkBool32 + shaderRoundingModeRTZFloat64 : VkBool32 + + +@dataclass +class VkPhysicalDeviceVulkan11Properties: + deviceUUID : uint8_t*VK_UUID_SIZE + driverUUID : uint8_t*VK_UUID_SIZE + deviceLUID : uint8_t*VK_LUID_SIZE + deviceNodeMask : uint32_t + deviceLUIDValid : VkBool32 + subgroupSize : uint32_t + subgroupSupportedStages : VkShaderStageFlags + subgroupSupportedOperations : VkSubgroupFeatureFlags + subgroupQuadOperationsInAllStages : VkBool32 + pointClippingBehavior : VkPointClippingBehavior + maxMultiviewViewCount : uint32_t + maxMultiviewInstanceIndex :uint32_t + protectedNoFault : VkBool32 + maxPerSetDescriptors : uint32_t + maxMemoryAllocationSize : VkDeviceSize + + +@dataclass +class VkPhysicalDeviceVulkan11Features: + storageBuffer16BitAccess: VkBool32 + uniformAndStorageBuffer16BitAccess: VkBool32 + storagePushConstant16: VkBool32 + storageInputOutput16: VkBool32 + multiview: VkBool32 + multiviewGeometryShader: VkBool32 + multiviewTessellationShader: VkBool32 + variablePointersStorageBuffer: VkBool32 + variablePointers: VkBool32 + protectedMemory: VkBool32 + samplerYcbcrConversion: VkBool32 + shaderDrawParameters: VkBool32 + + +@dataclass +class VkPhysicalDeviceVulkan12Properties: + driverID: VkDriverId + driverName: str + driverInfo: str + conformanceVersion: ConformanceVersion + denormBehaviorIndependence: VkShaderFloatControlsIndependence + roundingModeIndependence: VkShaderFloatControlsIndependence + shaderSignedZeroInfNanPreserveFloat16: VkBool32 + shaderSignedZeroInfNanPreserveFloat32: VkBool32 + shaderSignedZeroInfNanPreserveFloat64: VkBool32 + shaderDenormPreserveFloat16: VkBool32 + shaderDenormPreserveFloat32: VkBool32 + shaderDenormPreserveFloat64: VkBool32 + shaderDenormFlushToZeroFloat16: VkBool32 + shaderDenormFlushToZeroFloat32: VkBool32 + shaderDenormFlushToZeroFloat64: VkBool32 + shaderRoundingModeRTEFloat16: VkBool32 + shaderRoundingModeRTEFloat32: VkBool32 + shaderRoundingModeRTEFloat64: VkBool32 + shaderRoundingModeRTZFloat16: VkBool32 + shaderRoundingModeRTZFloat32: VkBool32 + shaderRoundingModeRTZFloat64: VkBool32 + maxUpdateAfterBindDescriptorsInAllPools: uint32_t + shaderUniformBufferArrayNonUniformIndexingNative: VkBool32 + shaderSampledImageArrayNonUniformIndexingNative: VkBool32 + shaderStorageBufferArrayNonUniformIndexingNative: VkBool32 + shaderStorageImageArrayNonUniformIndexingNative: VkBool32 + shaderInputAttachmentArrayNonUniformIndexingNative: VkBool32 + robustBufferAccessUpdateAfterBind: VkBool32 + quadDivergentImplicitLod: VkBool32 + maxPerStageDescriptorUpdateAfterBindSamplers: uint32_t + maxPerStageDescriptorUpdateAfterBindUniformBuffers: uint32_t + maxPerStageDescriptorUpdateAfterBindStorageBuffers: uint32_t + maxPerStageDescriptorUpdateAfterBindSampledImages: uint32_t + maxPerStageDescriptorUpdateAfterBindStorageImages: uint32_t + maxPerStageDescriptorUpdateAfterBindInputAttachments: uint32_t + maxPerStageUpdateAfterBindResources: uint32_t + maxDescriptorSetUpdateAfterBindSamplers: uint32_t + maxDescriptorSetUpdateAfterBindUniformBuffers: uint32_t + maxDescriptorSetUpdateAfterBindUniformBuffersDynamic: uint32_t + maxDescriptorSetUpdateAfterBindStorageBuffers: uint32_t + maxDescriptorSetUpdateAfterBindStorageBuffersDynamic: uint32_t + maxDescriptorSetUpdateAfterBindSampledImages: uint32_t + maxDescriptorSetUpdateAfterBindStorageImages: uint32_t + maxDescriptorSetUpdateAfterBindInputAttachments: uint32_t + supportedDepthResolveModes: VkResolveModeFlags + supportedStencilResolveModes: VkResolveModeFlags + independentResolveNone: VkBool32 + independentResolve: VkBool32 + filterMinmaxSingleComponentFormats: VkBool32 + filterMinmaxImageComponentMapping: VkBool32 + maxTimelineSemaphoreValueDifference: uint64_t + framebufferIntegerColorSampleCounts: VkSampleCountFlags + + +@dataclass +class VkPhysicalDeviceVulkan12Features: + samplerMirrorClampToEdge: VkBool32 + drawIndirectCount: VkBool32 + storageBuffer8BitAccess: VkBool32 + uniformAndStorageBuffer8BitAccess: VkBool32 + storagePushConstant8: VkBool32 + shaderBufferInt64Atomics: VkBool32 + shaderSharedInt64Atomics: VkBool32 + shaderFloat16: VkBool32 + shaderInt8: VkBool32 + descriptorIndexing: VkBool32 + shaderInputAttachmentArrayDynamicIndexing: VkBool32 + shaderUniformTexelBufferArrayDynamicIndexing: VkBool32 + shaderStorageTexelBufferArrayDynamicIndexing: VkBool32 + shaderUniformBufferArrayNonUniformIndexing: VkBool32 + shaderSampledImageArrayNonUniformIndexing: VkBool32 + shaderStorageBufferArrayNonUniformIndexing: VkBool32 + shaderStorageImageArrayNonUniformIndexing: VkBool32 + shaderInputAttachmentArrayNonUniformIndexing: VkBool32 + shaderUniformTexelBufferArrayNonUniformIndexing: VkBool32 + shaderStorageTexelBufferArrayNonUniformIndexing: VkBool32 + descriptorBindingUniformBufferUpdateAfterBind: VkBool32 + descriptorBindingSampledImageUpdateAfterBind: VkBool32 + descriptorBindingStorageImageUpdateAfterBind: VkBool32 + descriptorBindingStorageBufferUpdateAfterBind: VkBool32 + descriptorBindingUniformTexelBufferUpdateAfterBind: VkBool32 + descriptorBindingStorageTexelBufferUpdateAfterBind: VkBool32 + descriptorBindingUpdateUnusedWhilePending: VkBool32 + descriptorBindingPartiallyBound: VkBool32 + descriptorBindingVariableDescriptorCount: VkBool32 + runtimeDescriptorArray: VkBool32 + samplerFilterMinmax: VkBool32 + scalarBlockLayout: VkBool32 + imagelessFramebuffer: VkBool32 + uniformBufferStandardLayout: VkBool32 + shaderSubgroupExtendedTypes: VkBool32 + separateDepthStencilLayouts: VkBool32 + hostQueryReset: VkBool32 + timelineSemaphore: VkBool32 + bufferDeviceAddress: VkBool32 + bufferDeviceAddressCaptureReplay: VkBool32 + bufferDeviceAddressMultiDevice: VkBool32 + vulkanMemoryModel: VkBool32 + vulkanMemoryModelDeviceScope: VkBool32 + vulkanMemoryModelAvailabilityVisibilityChains: VkBool32 + shaderOutputViewportIndex: VkBool32 + shaderOutputLayer: VkBool32 + subgroupBroadcastDynamicId: VkBool32 + + +@dataclass +class VkPhysicalDeviceVulkan13Properties: + minSubgroupSize: uint32_t + maxSubgroupSize: uint32_t + maxComputeWorkgroupSubgroups: uint32_t + requiredSubgroupSizeStages: VkShaderStageFlags + maxInlineUniformBlockSize: uint32_t + maxPerStageDescriptorInlineUniformBlocks: uint32_t + maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks: uint32_t + maxDescriptorSetInlineUniformBlocks: uint32_t + maxDescriptorSetUpdateAfterBindInlineUniformBlocks: uint32_t + maxInlineUniformTotalSize: uint32_t + integerDotProduct8BitUnsignedAccelerated: VkBool32 + integerDotProduct8BitSignedAccelerated: VkBool32 + integerDotProduct8BitMixedSignednessAccelerated: VkBool32 + integerDotProduct4x8BitPackedUnsignedAccelerated: VkBool32 + integerDotProduct4x8BitPackedSignedAccelerated: VkBool32 + integerDotProduct4x8BitPackedMixedSignednessAccelerated: VkBool32 + integerDotProduct16BitUnsignedAccelerated: VkBool32 + integerDotProduct16BitSignedAccelerated: VkBool32 + integerDotProduct16BitMixedSignednessAccelerated: VkBool32 + integerDotProduct32BitUnsignedAccelerated: VkBool32 + integerDotProduct32BitSignedAccelerated: VkBool32 + integerDotProduct32BitMixedSignednessAccelerated: VkBool32 + integerDotProduct64BitUnsignedAccelerated: VkBool32 + integerDotProduct64BitSignedAccelerated: VkBool32 + integerDotProduct64BitMixedSignednessAccelerated: VkBool32 + integerDotProductAccumulatingSaturating8BitUnsignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating8BitSignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated: VkBool32 + integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated: VkBool32 + integerDotProductAccumulatingSaturating16BitUnsignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating16BitSignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated: VkBool32 + integerDotProductAccumulatingSaturating32BitUnsignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating32BitSignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated: VkBool32 + integerDotProductAccumulatingSaturating64BitUnsignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating64BitSignedAccelerated: VkBool32 + integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated: VkBool32 + storageTexelBufferOffsetAlignmentBytes: VkDeviceSize + storageTexelBufferOffsetSingleTexelAlignment: VkBool32 + uniformTexelBufferOffsetAlignmentBytes: VkDeviceSize + uniformTexelBufferOffsetSingleTexelAlignment: VkBool32 + maxBufferSize: VkDeviceSize + + +@dataclass +class VkPhysicalDeviceVulkan13Features: + robustImageAccess: VkBool32 + inlineUniformBlock: VkBool32 + descriptorBindingInlineUniformBlockUpdateAfterBind: VkBool32 + pipelineCreationCacheControl: VkBool32 + privateData: VkBool32 + shaderDemoteToHelperInvocation: VkBool32 + shaderTerminateInvocation: VkBool32 + subgroupSizeControl: VkBool32 + computeFullSubgroups: VkBool32 + synchronization2: VkBool32 + textureCompressionASTC_HDR: VkBool32 + shaderZeroInitializeWorkgroupMemory: VkBool32 + dynamicRendering: VkBool32 + shaderIntegerDotProduct: VkBool32 + maintenance4: VkBool32 + + +@dataclass +class VkPhysicalDeviceVulkan14Properties: + lineSubPixelPrecisionBits: uint32_t + maxVertexAttribDivisor: uint32_t + supportsNonZeroFirstInstance: VkBool32 + maxPushDescriptors: uint32_t + dynamicRenderingLocalReadDepthStencilAttachments: VkBool32 + dynamicRenderingLocalReadMultisampledAttachments: VkBool32 + earlyFragmentMultisampleCoverageAfterSampleCounting: VkBool32 + earlyFragmentSampleMaskTestBeforeSampleCounting: VkBool32 + depthStencilSwizzleOneSupport: VkBool32 + polygonModePointSize: VkBool32 + nonStrictSinglePixelWideLinesUseParallelogram: VkBool32 + nonStrictWideLinesUseParallelogram: VkBool32 + blockTexelViewCompatibleMultipleLayers: VkBool32 + maxCombinedImageSamplerDescriptorCount: uint32_t + fragmentShadingRateClampCombinerInputs: VkBool32 + defaultRobustnessStorageBuffers: VkPipelineRobustnessBufferBehavior + defaultRobustnessUniformBuffers: VkPipelineRobustnessBufferBehavior + defaultRobustnessVertexInputs: VkPipelineRobustnessBufferBehavior + defaultRobustnessImages: VkPipelineRobustnessBufferBehavior + copySrcLayoutCount: uint32_t + pCopySrcLayouts: List[VkImageLayout] + copyDstLayoutCount: uint32_t + pCopyDstLayouts: List[VkImageLayout] + optimalTilingLayoutUUID: uint8_t + identicalMemoryTypeRequirements: VkBool32 + + +@dataclass +class VkPhysicalDeviceVulkan14Features: + globalPriorityQuery: VkBool32 + shaderSubgroupRotate: VkBool32 + shaderSubgroupRotateClustered: VkBool32 + shaderFloatControls2: VkBool32 + shaderExpectAssume: VkBool32 + rectangularLines: VkBool32 + bresenhamLines: VkBool32 + smoothLines: VkBool32 + stippledRectangularLines: VkBool32 + stippledBresenhamLines: VkBool32 + stippledSmoothLines: VkBool32 + vertexAttributeInstanceRateDivisor: VkBool32 + vertexAttributeInstanceRateZeroDivisor: VkBool32 + indexTypeUint8: VkBool32 + dynamicRenderingLocalRead: VkBool32 + maintenance5: VkBool32 + maintenance6: VkBool32 + pipelineProtectedAccess: VkBool32 + pipelineRobustness: VkBool32 + hostImageCopy: VkBool32 + # pushDescriptor: bool + + +@dataclass +class VkPhysicalDeviceDriverProperties: + driverID: VkDriverId + driverName: str + driverInfo: str + conformanceVersion: ConformanceVersion + +# Defining alias for structures +VkPhysicalDeviceLineRasterizationFeaturesEXT = VkPhysicalDeviceLineRasterizationFeatures +VkPhysicalDeviceLineRasterizationFeaturesKHR = VkPhysicalDeviceLineRasterizationFeatures +VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR = VkPhysicalDeviceShaderIntegerDotProductFeatures +VkPhysicalDevice8BitStorageFeaturesKHR = VkPhysicalDevice8BitStorageFeatures +VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR = VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures +VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR = VkPhysicalDeviceVertexAttributeDivisorFeatures +VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT = VkPhysicalDeviceVertexAttributeDivisorFeatures +VkPhysicalDeviceIndexTypeUint8FeaturesKHR = VkPhysicalDeviceIndexTypeUint8Features +VkPhysicalDeviceIndexTypeUint8FeaturesEXT = VkPhysicalDeviceIndexTypeUint8Features +VkPhysicalDeviceVariablePointerFeatures = VkPhysicalDeviceVariablePointersFeatures +VkPhysicalDeviceVariablePointersFeaturesKHR = VkPhysicalDeviceVariablePointersFeatures +VkPhysicalDeviceVariablePointerFeaturesKHR = VkPhysicalDeviceVariablePointersFeatures +VkPhysicalDeviceFloat16Int8FeaturesKHR = VkPhysicalDeviceShaderFloat16Int8Features +VkPhysicalDeviceShaderFloat16Int8FeaturesKHR = VkPhysicalDeviceShaderFloat16Int8Features +VkPhysicalDeviceFloatControlsPropertiesKHR = VkPhysicalDeviceFloatControlsProperties +VkPhysicalDeviceShaderDrawParametersFeatures = VkPhysicalDeviceShaderDrawParameterFeatures +VkPhysicalDeviceDriverPropertiesKHR = VkPhysicalDeviceDriverProperties + +# Defining dependency of structures on extensions +VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING = { + "extensions": { + "VK_KHR_variable_pointers": [ + { "VkPhysicalDeviceVariablePointerFeaturesKHR": "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES" }, + { "VkPhysicalDeviceVariablePointersFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES"}, + ], + "VK_KHR_shader_float16_int8": [ + { "VkPhysicalDeviceShaderFloat16Int8FeaturesKHR": "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES" }, + {"VkPhysicalDeviceFloat16Int8FeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES"}, + ], + "VK_EXT_image_2d_view_of_3d" : [ + {"VkPhysicalDeviceImage2DViewOf3DFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT"}, + ], + "VK_EXT_custom_border_color" : [ + {"VkPhysicalDeviceCustomBorderColorFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT"}, + ], + "VK_EXT_primitive_topology_list_restart": [ + {"VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT"}, + ], + "VK_EXT_provoking_vertex" : [ + {"VkPhysicalDeviceProvokingVertexFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT"}, + ], + "VK_KHR_index_type_uint8" : [ + {"VkPhysicalDeviceIndexTypeUint8FeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES"}, + ], + "VK_EXT_index_type_uint8" : [ + {"VkPhysicalDeviceIndexTypeUint8FeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES"}, + ], + "VK_KHR_vertex_attribute_divisor" : [ + {"VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES"}, + ], + "VK_EXT_vertex_attribute_divisor" : [ + {"VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES"}, + ], + "VK_EXT_transform_feedback" : [ + {"VkPhysicalDeviceTransformFeedbackFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT"}, + ], + "VK_KHR_shader_subgroup_uniform_control_flow" : [ + {"VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR"}, + ], + "VK_KHR_shader_subgroup_extended_types" : [ + {"VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES"}, + ], + "VK_KHR_8bit_storage" : [ + {"VkPhysicalDevice8BitStorageFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES"}, + ], + "VK_KHR_shader_integer_dot_product" : [ + {"VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES"}, + ], + "VK_IMG_relaxed_line_rasterization" : [ + {"VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG"}, + ], + "VK_KHR_line_rasterization" : [ + {"VkPhysicalDeviceLineRasterizationFeaturesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES"}, + ], + "VK_EXT_line_rasterization" : [ + {"VkPhysicalDeviceLineRasterizationFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES"}, + ], + "VK_EXT_primitives_generated_query" : [ + {"VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT"}, + ], + "VK_KHR_shader_float_controls" : [ + {"VkPhysicalDeviceFloatControlsPropertiesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES"}, + ], + "VK_KHR_driver_properties" : [ + {"VkPhysicalDeviceDriverPropertiesKHR" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES"}, + ] + } +} + +# Defining dependency of structures on vulkan cores +VULKAN_CORES_AND_STRUCTS_MAPPING = { + "versions" : { + "Core11" : [ + {"VkPhysicalDeviceVulkan11Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES"}, + {"VkPhysicalDeviceVulkan11Features" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES"}, + ], + "Core12" : [ + {"VkPhysicalDeviceVulkan12Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES"}, + {"VkPhysicalDeviceVulkan12Features" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES"}, + ], + "Core13" : [ + {"VkPhysicalDeviceVulkan13Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES"}, + {"VkPhysicalDeviceVulkan13Features" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES"}, + ], + "Core14" : [ + {"VkPhysicalDeviceVulkan14Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_PROPERTIES"}, + {"VkPhysicalDeviceVulkan14Features" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES"}, + ] + } +} + +# Defining map for list type members mapped to its size +LIST_TYPE_FIELD_AND_SIZE_MAPPING = { + "pCopySrcLayouts": "copySrcLayoutCount", + "pCopyDstLayouts": "copyDstLayoutCount", + "memoryTypes": "memoryTypeCount", + "memoryHeaps": "memoryHeapCount", +} + +# Defining dependency of structures on vulkan api version +VULKAN_VERSIONS_AND_STRUCTS_MAPPING = { + "VK_VERSION_1_0" : [ + {"VkPhysicalDeviceProperties" : "" }, + {"VkPhysicalDeviceFeatures" : ""}, + {"VkPhysicalDeviceMemoryProperties" : ""}, + ], + "VK_VERSION_1_1" : [ + {"VkPhysicalDeviceSubgroupProperties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES"}, + {"VkPhysicalDevicePointClippingProperties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES"}, + {"VkPhysicalDeviceMultiviewProperties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES"}, + {"VkPhysicalDeviceIDProperties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES"}, + {"VkPhysicalDeviceMaintenance3Properties" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES"}, + {"VkPhysicalDeviceMultiviewFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES"}, + {"VkPhysicalDeviceVariablePointersFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES"}, + {"VkPhysicalDeviceProtectedMemoryFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES"}, + {"VkPhysicalDeviceSamplerYcbcrConversionFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES"}, + {"VkPhysicalDeviceShaderDrawParameterFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES"}, + {"VkPhysicalDevice16BitStorageFeatures" : "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES"}, + ] +} + +# List of structures that are not dependent on extensions +EXTENSION_INDEPENDENT_STRUCTS = [ + VkPhysicalDeviceProperties, + VkPhysicalDeviceFeatures, + VkPhysicalDeviceMemoryProperties, + VkPhysicalDeviceSubgroupProperties, + VkPhysicalDevicePointClippingProperties, + VkPhysicalDeviceMultiviewProperties, + VkPhysicalDeviceIDProperties, + VkPhysicalDeviceMaintenance3Properties, + VkPhysicalDevice16BitStorageFeatures, + VkPhysicalDeviceMultiviewFeatures, + VkPhysicalDeviceVariablePointersFeatures, + VkPhysicalDeviceProtectedMemoryFeatures, + VkPhysicalDeviceSamplerYcbcrConversionFeatures, + VkPhysicalDeviceShaderDrawParameterFeatures, +] + +# List of all the structures for vkjson +ALL_STRUCTS = [ + VkPhysicalDeviceFloatControlsPropertiesKHR, + VkPhysicalDeviceProperties, + VkPhysicalDeviceMemoryProperties, + VkPhysicalDeviceSubgroupProperties, + VkPhysicalDevicePointClippingProperties, + VkPhysicalDeviceMultiviewProperties, + VkPhysicalDeviceIDProperties, + VkPhysicalDeviceMaintenance3Properties, + VkPhysicalDeviceSparseProperties, + VkImageFormatProperties, + VkQueueFamilyProperties, + VkExtensionProperties, + VkLayerProperties, + VkFormatProperties, + VkPhysicalDeviceVariablePointerFeaturesKHR, + VkPhysicalDeviceVariablePointersFeaturesKHR, + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR, + VkPhysicalDeviceFloat16Int8FeaturesKHR, + VkPhysicalDeviceImage2DViewOf3DFeaturesEXT, + VkPhysicalDeviceCustomBorderColorFeaturesEXT, + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT, + VkPhysicalDeviceProvokingVertexFeaturesEXT, + VkPhysicalDeviceIndexTypeUint8FeaturesKHR, + VkPhysicalDeviceIndexTypeUint8FeaturesEXT, + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR, + VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT, + VkPhysicalDeviceTransformFeedbackFeaturesEXT, + VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR, + VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR, + VkPhysicalDevice8BitStorageFeaturesKHR, + VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR, + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG, + VkPhysicalDeviceLineRasterizationFeaturesKHR, + VkPhysicalDeviceLineRasterizationFeaturesEXT, + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT, + VkPhysicalDevice16BitStorageFeatures, + VkPhysicalDeviceMultiviewFeatures, + VkPhysicalDeviceProtectedMemoryFeatures, + VkPhysicalDeviceSamplerYcbcrConversionFeatures, + VkPhysicalDeviceShaderDrawParameterFeatures, + VkPhysicalDeviceLimits, + VkPhysicalDeviceFeatures, + VkPhysicalDeviceVulkan11Properties, + VkPhysicalDeviceVulkan11Features, + VkPhysicalDeviceVulkan12Properties, + VkPhysicalDeviceVulkan12Features, + VkPhysicalDeviceVulkan13Properties, + VkPhysicalDeviceVulkan13Features, + VkPhysicalDeviceVulkan14Properties, + VkPhysicalDeviceVulkan14Features, + VkPhysicalDeviceDriverProperties, +] diff --git a/vulkan/scripts/vkjson_generator.py b/vulkan/scripts/vkjson_generator.py new file mode 100644 index 0000000000..7dc55d9703 --- /dev/null +++ b/vulkan/scripts/vkjson_generator.py @@ -0,0 +1,1882 @@ +#!/usr/bin/env python3 +# +# Copyright 2025 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. + +"""Generates the vkjson files. +""" +import dataclasses +import os +import re +from typing import get_origin + +import generator_common as gencom +import vk as VK + +dataclass_field = dataclasses.field + + +COPYRIGHT_WARNINGS = """/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 The Khronos Group Inc. +// Copyright (c) 2015-2016 Valve Corporation +// Copyright (c) 2015-2016 LunarG, Inc. +// Copyright (c) 2015-2016 Google, Inc. +// +// 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. +/////////////////////////////////////////////////////////////////////////////// +""" + + +def get_copyright_warnings(): + return COPYRIGHT_WARNINGS + + +def get_vkjson_struct_name(extension_name): + """Gets the corresponding structure name from a Vulkan extension name. + Example: "VK_KHR_shader_float16_int8" → "VkJsonKHRShaderFloat16Int8" + """ + prefix_map = { + "VK_KHR": "VkJsonKHR", + "VK_EXT": "VkJsonExt", + "VK_IMG": "VkJsonIMG" + } + + for prefix, replacement in prefix_map.items(): + if extension_name.startswith(prefix): + struct_name = replacement + extension_name[len(prefix):] + break + else: + struct_name = f"VkJsonExt{extension_name}" + + # Convert underscores to camel case + # Example: "VK_KHR_shader_float16_int8" → "VkJsonKHRShaderFloat16Int8" + struct_name = re.sub(r"_(.)", lambda m: m.group(1).upper(), struct_name) + + return struct_name + + +def get_vkjson_struct_variable_name(extension_name): + """Gets corresponding instance name from a Vulkan extension name. + Example: "VK_KHR_shader_float16_int8" → "khr_shader_float16_int8" + """ + prefix_map = { + "VK_KHR_": "khr_", + "VK_EXT_": "ext_", + "VK_IMG_": "img_" + } + + for prefix, replacement in prefix_map.items(): + if extension_name.startswith(prefix): + return replacement + extension_name[len(prefix):] + + return extension_name.lower() # Default case if no known prefix matches + + +def get_struct_name(struct_name): + """Gets corresponding instance name + Example: "VkPhysicalDeviceShaderFloat16Int8FeaturesKHR" → "shader_float16_int8_features_khr" + """ + # Remove "VkPhysicalDevice" prefix and any of the known suffixes + base_name = struct_name.removeprefix("VkPhysicalDevice").removesuffix("KHR").removesuffix("EXT").removesuffix("IMG") + + # Convert CamelCase to snake_case + # Example: "ShaderFloat16Int8Features" → "shader_float16_int8_features" + variable_name = re.sub(r"(?<!^)(?=[A-Z])", "_", base_name).lower() + + # Fix special cases + variable_name = variable_name.replace("2_d_", "_2d_").replace("3_d_", "_3d_") + + # Add back the correct suffix if it was removed + suffix_map = {"KHR": "_khr", "EXT": "_ext", "IMG": "_img"} + for suffix, replacement in suffix_map.items(): + if struct_name.endswith(suffix): + variable_name += replacement + break + + # Handle specific exceptions + special_cases = { + "8_bit_storage_features_khr": "bit8_storage_features_khr", + "memory_properties": "memory", + "16_bit_storage_features": "bit16_storage_features", + "i_d_properties": "id_properties" + } + + return special_cases.get(variable_name, variable_name) + + +def generate_extension_struct_definition(f): + """Generates struct definition code for extension based structs + Example: + struct VkJsonKHRShaderFloatControls { + VkJsonKHRShaderFloatControls() { + reported = false; + memset(&float_controls_properties_khr, 0, + sizeof(VkPhysicalDeviceFloatControlsPropertiesKHR)); + } + bool reported; + VkPhysicalDeviceFloatControlsPropertiesKHR float_controls_properties_khr; + }; + """ + vkJson_entries = [] + + for extension_name, struct_list in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items(): + vkjson_struct_name = get_vkjson_struct_name(extension_name) + vkjson_struct_variable_name = get_vkjson_struct_variable_name(extension_name) + vkJson_entries.append(f"{vkjson_struct_name} {vkjson_struct_variable_name}") + + struct_entries = [] + + f.write(f"struct {vkjson_struct_name} {{\n") + f.write(f" {vkjson_struct_name}() {{\n") + f.write(" reported = false;\n") + + for struct_map in struct_list: + for struct_name, _ in struct_map.items(): + variable_name = get_struct_name(struct_name) + f.write(f" memset(&{variable_name}, 0, sizeof({struct_name}));\n") + struct_entries.append(f"{struct_name} {variable_name}") + + f.write(" }\n") # End of constructor + f.write(" bool reported;\n") + + for entry in struct_entries: + f.write(f" {entry};\n") + + f.write("};\n\n") # End of struct + + return vkJson_entries + + +def generate_vk_core_struct_definition(f): + """Generates struct definition code for vulkan cores + Example: + struct VkJsonCore11 { + VkPhysicalDeviceVulkan11Properties properties; + VkPhysicalDeviceVulkan11Features features; + }; + """ + vkJson_core_entries = [] + + for version, items in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].items(): + struct_name = f"VkJson{version}" + vkJson_core_entries.append(f"{struct_name} {version.lower()}") + + f.write(f"struct {struct_name} {{\n") + + for item in items: + for struct_type, _ in item.items(): + field_name = "properties" if "Properties" in struct_type else "features" + f.write(f" {struct_type} {field_name};\n") + + f.write("};\n\n") + + return vkJson_core_entries + + +def generate_memset_statements(f): + """Generates memset statements for all independent Vulkan structs and core Vulkan versions. + This initializes struct instances to zero before use. + + Example: + memset(&properties, 0, sizeof(VkPhysicalDeviceProperties)); + VkPhysicalDeviceProperties properties; + """ + entries = [] + + # Process independent structs + for dataclass_type in VK.EXTENSION_INDEPENDENT_STRUCTS: + class_name = dataclass_type.__name__ + variable_name = get_struct_name(class_name) + f.write(f"memset(&{variable_name}, 0, sizeof({class_name}));\n") + entries.append(f"{class_name} {variable_name}") + + # Process vulkan core structs + for version in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"]: + struct_name = f"VkJson{version}" + f.write(f"memset(&{version.lower()}, 0, sizeof({struct_name}));\n") + + return entries + + +def gen_h(): + """Generates vkjson.h file. + """ + genfile = os.path.join(os.path.dirname(__file__), + "..", "vkjson", "vkjson.h") + + with open(genfile, "w") as f: + f.write(f'{get_copyright_warnings()}\n') + + f.write("""\ +#ifndef VKJSON_H_ +#define VKJSON_H_ + +#include <string.h> +#include <vulkan/vulkan.h> + +#include <map> +#include <string> +#include <vector> + +#ifdef WIN32 +#undef min +#undef max +#endif + +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ +struct VkJsonLayer { + VkLayerProperties properties; + std::vector<VkExtensionProperties> extensions; +}; + +\n""") + + vkjson_extension_structs = generate_extension_struct_definition(f) + vkjson_core_structs = generate_vk_core_struct_definition(f) + + f.write("""\ +struct VkJsonDevice { + VkJsonDevice() {""") + + feature_property_structs = generate_memset_statements(f) + + f.write("""\ + }\n""") + for struct_entries in (vkjson_extension_structs, vkjson_core_structs, feature_property_structs): + for entry in struct_entries: + f.write(entry + ";\n") + + f.write("""\ + std::vector<VkQueueFamilyProperties> queues; + std::vector<VkExtensionProperties> extensions; + std::vector<VkLayerProperties> layers; + std::map<VkFormat, VkFormatProperties> formats; + std::map<VkExternalFenceHandleTypeFlagBits, VkExternalFenceProperties> + external_fence_properties; + std::map<VkExternalSemaphoreHandleTypeFlagBits, VkExternalSemaphoreProperties> + external_semaphore_properties; +}; + +struct VkJsonDeviceGroup { + VkJsonDeviceGroup() { + memset(&properties, 0, sizeof(VkPhysicalDeviceGroupProperties)); + } + VkPhysicalDeviceGroupProperties properties; + std::vector<uint32_t> device_inds; +}; + +struct VkJsonInstance { + VkJsonInstance() : api_version(0) {} + uint32_t api_version; + std::vector<VkJsonLayer> layers; + std::vector<VkExtensionProperties> extensions; + std::vector<VkJsonDevice> devices; + std::vector<VkJsonDeviceGroup> device_groups; +}; + +VkJsonInstance VkJsonGetInstance(); +std::string VkJsonInstanceToJson(const VkJsonInstance& instance); +bool VkJsonInstanceFromJson(const std::string& json, + VkJsonInstance* instance, + std::string* errors); + +VkJsonDevice VkJsonGetDevice(VkPhysicalDevice device); +std::string VkJsonDeviceToJson(const VkJsonDevice& device); +bool VkJsonDeviceFromJson(const std::string& json, + VkJsonDevice* device, + std::string* errors); + +std::string VkJsonImageFormatPropertiesToJson( + const VkImageFormatProperties& properties); +bool VkJsonImageFormatPropertiesFromJson(const std::string& json, + VkImageFormatProperties* properties, + std::string* errors); + +// Backward-compatibility aliases +typedef VkJsonDevice VkJsonAllProperties; +inline VkJsonAllProperties VkJsonGetAllProperties( + VkPhysicalDevice physicalDevice) { + return VkJsonGetDevice(physicalDevice); +} +inline std::string VkJsonAllPropertiesToJson( + const VkJsonAllProperties& properties) { + return VkJsonDeviceToJson(properties); +} +inline bool VkJsonAllPropertiesFromJson(const std::string& json, + VkJsonAllProperties* properties, + std::string* errors) { + return VkJsonDeviceFromJson(json, properties, errors); +} + +#endif // VKJSON_H_""") + + f.close() + gencom.run_clang_format(genfile) + + +def generate_extension_struct_template(): + """Generates templates for extensions + Example: + template <typename Visitor> + inline bool Iterate(Visitor* visitor, VkJsonKHRVariablePointers* structs) { + return visitor->Visit("variablePointerFeaturesKHR", + &structs->variable_pointer_features_khr) && + visitor->Visit("variablePointersFeaturesKHR", + &structs->variable_pointers_features_khr); + } + """ + template_code = [] + + for extension, struct_mappings in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items(): + struct_type = get_vkjson_struct_name(extension) + + template_code.append(f"template <typename Visitor>") + template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_type}* structs) {{") + template_code.append(" return ") + + visitor_calls = [] + for struct_map in struct_mappings: + for struct_name in struct_map: + json_field_name = struct_name.replace("VkPhysicalDevice", "") + json_field_name = json_field_name[0].lower() + json_field_name[1:] + + # Special case renaming + if json_field_name == "8BitStorageFeaturesKHR": + json_field_name = "bit8StorageFeaturesKHR" + + visitor_calls.append( + f'visitor->Visit("{json_field_name}", &structs->{get_struct_name(struct_name)})' + ) + + template_code.append(" &&\n ".join(visitor_calls) + ";") + template_code.append("}\n") + + return "\n".join(template_code) + + +def generate_core_template(): + """Generates templates for vulkan cores. + template <typename Visitor> + inline bool Iterate(Visitor* visitor, VkJsonCore11* core) { + return visitor->Visit("properties", &core->properties) && + visitor->Visit("features", &core->features); + } + """ + template_code = [] + + for version, struct_list in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].items(): + struct_type = f"VkJson{version}" + + template_code.append(f"template <typename Visitor>") + template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_type}* core) {{") + template_code.append(" return") + + visitor_calls = [] + for struct_map in struct_list: + for struct_name in struct_map: + member_name = "properties" if "Properties" in struct_name else "features" + visitor_calls.append(f'visitor->Visit("{member_name}", &core->{member_name})') + + template_code.append(" &&\n ".join(visitor_calls) + ";") + template_code.append("}\n") + + return "\n".join(template_code) + + +def generate_struct_template(data_classes): + """Generates templates for all the structs + template <typename Visitor> + inline bool Iterate(Visitor* visitor, + VkPhysicalDevicePointClippingProperties* properties) { + return visitor->Visit("pointClippingBehavior", + &properties->pointClippingBehavior); + } + """ + template_code = [] + processed_classes = set() # Track processed class names + + for dataclass_type in data_classes: + struct_name = dataclass_type.__name__ + + if struct_name in processed_classes: + continue # Skip already processed struct + processed_classes.add(struct_name) + + struct_fields = dataclasses.fields(dataclass_type) + template_code.append("template <typename Visitor>") + + # Determine the correct variable name based on the struct type + struct_var = "properties" if "Properties" in struct_name else "features" if "Features" in struct_name else "limits" if "Limits" in struct_name else None + + if not struct_var: + continue # Skip structs that don't match expected patterns + + template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_name}* {struct_var}) {{") + template_code.append(f"return\n") + + visitor_calls = [] + for struct_field in struct_fields: + field_name = struct_field.name + field_type = struct_field.type + + if get_origin(field_type) is list: + # Handle list types (VisitArray) + size_field_name = VK.LIST_TYPE_FIELD_AND_SIZE_MAPPING[field_name] + visitor_calls.append(f'visitor->VisitArray("{field_name}", {struct_var}->{size_field_name}, &{struct_var}->{field_name})') + else: + # Handle other types (Visit) + visitor_calls.append(f'visitor->Visit("{field_name}", &{struct_var}->{field_name})') + + template_code.append(" &&\n ".join(visitor_calls) + ";") + template_code.append("}\n\n") + + return "\n".join(template_code) + + +def emit_struct_visits_by_vk_version(f, version): + """Emits visitor calls for Vulkan version structs + """ + for struct_map in VK.VULKAN_VERSIONS_AND_STRUCTS_MAPPING[version]: + for struct_name, _ in struct_map.items(): + struct_var = get_struct_name(struct_name) + # Converts struct_var from snake_case (e.g., point_clipping_properties) + # to camelCase (e.g., pointClippingProperties) for struct_display_name. + struct_display_name = re.sub(r"_([a-z])", lambda match: match.group(1).upper(), struct_var) + f.write(f'visitor->Visit("{struct_display_name}", &device->{struct_var}) &&\n') + + +def gen_cc(): + """Generates vkjson.cc file. + """ + genfile = os.path.join(os.path.dirname(__file__), + "..", "vkjson", "vkjson.cc") + + with open(genfile, "w") as f: + + f.write(get_copyright_warnings()) + f.write("\n") + + f.write("""\ +#include "vkjson.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <json/json.h> + +#include <algorithm> +#include <cinttypes> +#include <cmath> +#include <cstdio> +#include <limits> +#include <memory> +#include <sstream> +#include <type_traits> +#include <utility> + +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ +namespace { + +/* + * Annotation to tell clang that we intend to fall through from one case to + * another in a switch. Sourced from android-base/macros.h. + */ +#define FALLTHROUGH_INTENDED [[clang::fallthrough]] + +inline bool IsIntegral(double value) { +#if defined(ANDROID) + // Android NDK doesn't provide std::trunc yet + return trunc(value) == value; +#else + return std::trunc(value) == value; +#endif +} + +// Floating point fields of Vulkan structure use single precision. The string +// output of max double value in c++ will be larger than Java double's infinity +// value. Below fake double max/min values are only to serve the safe json text +// parsing in between C++ and Java, because Java json library simply cannot +// handle infinity. +static const double SAFE_DOUBLE_MAX = 0.99 * std::numeric_limits<double>::max(); +static const double SAFE_DOUBLE_MIN = -SAFE_DOUBLE_MAX; + +template <typename T> struct EnumTraits; +template <> struct EnumTraits<VkPhysicalDeviceType> { + static bool exist(uint32_t e) { + switch (e) { + case VK_PHYSICAL_DEVICE_TYPE_OTHER: + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + case VK_PHYSICAL_DEVICE_TYPE_CPU: + return true; + } + return false; + } +}; + +template <> struct EnumTraits<VkFormat> { + static bool exist(uint32_t e) { + switch (e) { + case VK_FORMAT_UNDEFINED: + case VK_FORMAT_R4G4_UNORM_PACK8: + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: + case VK_FORMAT_B4G4R4A4_UNORM_PACK16: + case VK_FORMAT_R5G6B5_UNORM_PACK16: + case VK_FORMAT_B5G6R5_UNORM_PACK16: + case VK_FORMAT_R5G5B5A1_UNORM_PACK16: + case VK_FORMAT_B5G5R5A1_UNORM_PACK16: + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: + case VK_FORMAT_R8_UNORM: + case VK_FORMAT_R8_SNORM: + case VK_FORMAT_R8_USCALED: + case VK_FORMAT_R8_SSCALED: + case VK_FORMAT_R8_UINT: + case VK_FORMAT_R8_SINT: + case VK_FORMAT_R8_SRGB: + case VK_FORMAT_R8G8_UNORM: + case VK_FORMAT_R8G8_SNORM: + case VK_FORMAT_R8G8_USCALED: + case VK_FORMAT_R8G8_SSCALED: + case VK_FORMAT_R8G8_UINT: + case VK_FORMAT_R8G8_SINT: + case VK_FORMAT_R8G8_SRGB: + case VK_FORMAT_R8G8B8_UNORM: + case VK_FORMAT_R8G8B8_SNORM: + case VK_FORMAT_R8G8B8_USCALED: + case VK_FORMAT_R8G8B8_SSCALED: + case VK_FORMAT_R8G8B8_UINT: + case VK_FORMAT_R8G8B8_SINT: + case VK_FORMAT_R8G8B8_SRGB: + case VK_FORMAT_B8G8R8_UNORM: + case VK_FORMAT_B8G8R8_SNORM: + case VK_FORMAT_B8G8R8_USCALED: + case VK_FORMAT_B8G8R8_SSCALED: + case VK_FORMAT_B8G8R8_UINT: + case VK_FORMAT_B8G8R8_SINT: + case VK_FORMAT_B8G8R8_SRGB: + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SNORM: + case VK_FORMAT_R8G8B8A8_USCALED: + case VK_FORMAT_R8G8B8A8_SSCALED: + case VK_FORMAT_R8G8B8A8_UINT: + case VK_FORMAT_R8G8B8A8_SINT: + case VK_FORMAT_R8G8B8A8_SRGB: + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SNORM: + case VK_FORMAT_B8G8R8A8_USCALED: + case VK_FORMAT_B8G8R8A8_SSCALED: + case VK_FORMAT_B8G8R8A8_UINT: + case VK_FORMAT_B8G8R8A8_SINT: + case VK_FORMAT_B8G8R8A8_SRGB: + case VK_FORMAT_A8B8G8R8_UNORM_PACK32: + case VK_FORMAT_A8B8G8R8_SNORM_PACK32: + case VK_FORMAT_A8B8G8R8_USCALED_PACK32: + case VK_FORMAT_A8B8G8R8_SSCALED_PACK32: + case VK_FORMAT_A8B8G8R8_UINT_PACK32: + case VK_FORMAT_A8B8G8R8_SINT_PACK32: + case VK_FORMAT_A8B8G8R8_SRGB_PACK32: + case VK_FORMAT_A2R10G10B10_UNORM_PACK32: + case VK_FORMAT_A2R10G10B10_SNORM_PACK32: + case VK_FORMAT_A2R10G10B10_USCALED_PACK32: + case VK_FORMAT_A2R10G10B10_SSCALED_PACK32: + case VK_FORMAT_A2R10G10B10_UINT_PACK32: + case VK_FORMAT_A2R10G10B10_SINT_PACK32: + case VK_FORMAT_A2B10G10R10_UNORM_PACK32: + case VK_FORMAT_A2B10G10R10_SNORM_PACK32: + case VK_FORMAT_A2B10G10R10_USCALED_PACK32: + case VK_FORMAT_A2B10G10R10_SSCALED_PACK32: + case VK_FORMAT_A2B10G10R10_UINT_PACK32: + case VK_FORMAT_A2B10G10R10_SINT_PACK32: + case VK_FORMAT_R16_UNORM: + case VK_FORMAT_R16_SNORM: + case VK_FORMAT_R16_USCALED: + case VK_FORMAT_R16_SSCALED: + case VK_FORMAT_R16_UINT: + case VK_FORMAT_R16_SINT: + case VK_FORMAT_R16_SFLOAT: + case VK_FORMAT_R16G16_UNORM: + case VK_FORMAT_R16G16_SNORM: + case VK_FORMAT_R16G16_USCALED: + case VK_FORMAT_R16G16_SSCALED: + case VK_FORMAT_R16G16_UINT: + case VK_FORMAT_R16G16_SINT: + case VK_FORMAT_R16G16_SFLOAT: + case VK_FORMAT_R16G16B16_UNORM: + case VK_FORMAT_R16G16B16_SNORM: + case VK_FORMAT_R16G16B16_USCALED: + case VK_FORMAT_R16G16B16_SSCALED: + case VK_FORMAT_R16G16B16_UINT: + case VK_FORMAT_R16G16B16_SINT: + case VK_FORMAT_R16G16B16_SFLOAT: + case VK_FORMAT_R16G16B16A16_UNORM: + case VK_FORMAT_R16G16B16A16_SNORM: + case VK_FORMAT_R16G16B16A16_USCALED: + case VK_FORMAT_R16G16B16A16_SSCALED: + case VK_FORMAT_R16G16B16A16_UINT: + case VK_FORMAT_R16G16B16A16_SINT: + case VK_FORMAT_R16G16B16A16_SFLOAT: + case VK_FORMAT_R32_UINT: + case VK_FORMAT_R32_SINT: + case VK_FORMAT_R32_SFLOAT: + case VK_FORMAT_R32G32_UINT: + case VK_FORMAT_R32G32_SINT: + case VK_FORMAT_R32G32_SFLOAT: + case VK_FORMAT_R32G32B32_UINT: + case VK_FORMAT_R32G32B32_SINT: + case VK_FORMAT_R32G32B32_SFLOAT: + case VK_FORMAT_R32G32B32A32_UINT: + case VK_FORMAT_R32G32B32A32_SINT: + case VK_FORMAT_R32G32B32A32_SFLOAT: + case VK_FORMAT_R64_UINT: + case VK_FORMAT_R64_SINT: + case VK_FORMAT_R64_SFLOAT: + case VK_FORMAT_R64G64_UINT: + case VK_FORMAT_R64G64_SINT: + case VK_FORMAT_R64G64_SFLOAT: + case VK_FORMAT_R64G64B64_UINT: + case VK_FORMAT_R64G64B64_SINT: + case VK_FORMAT_R64G64B64_SFLOAT: + case VK_FORMAT_R64G64B64A64_UINT: + case VK_FORMAT_R64G64B64A64_SINT: + case VK_FORMAT_R64G64B64A64_SFLOAT: + case VK_FORMAT_B10G11R11_UFLOAT_PACK32: + case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: + case VK_FORMAT_D16_UNORM: + case VK_FORMAT_X8_D24_UNORM_PACK32: + case VK_FORMAT_D32_SFLOAT: + case VK_FORMAT_S8_UINT: + case VK_FORMAT_D16_UNORM_S8_UINT: + case VK_FORMAT_D24_UNORM_S8_UINT: + case VK_FORMAT_D32_SFLOAT_S8_UINT: + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: + case VK_FORMAT_BC2_UNORM_BLOCK: + case VK_FORMAT_BC2_SRGB_BLOCK: + case VK_FORMAT_BC3_UNORM_BLOCK: + case VK_FORMAT_BC3_SRGB_BLOCK: + case VK_FORMAT_BC4_UNORM_BLOCK: + case VK_FORMAT_BC4_SNORM_BLOCK: + case VK_FORMAT_BC5_UNORM_BLOCK: + case VK_FORMAT_BC5_SNORM_BLOCK: + case VK_FORMAT_BC6H_UFLOAT_BLOCK: + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + case VK_FORMAT_BC7_UNORM_BLOCK: + case VK_FORMAT_BC7_SRGB_BLOCK: + case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + case VK_FORMAT_EAC_R11_UNORM_BLOCK: + case VK_FORMAT_EAC_R11_SNORM_BLOCK: + case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: + case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: + case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: + case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: + case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: + case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: + case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: + case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: + case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: + case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: + case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: + case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: + case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: + case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: + case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: + case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: + case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: + case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: + case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: + case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: + case VK_FORMAT_G8B8G8R8_422_UNORM: + case VK_FORMAT_B8G8R8G8_422_UNORM: + case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: + case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: + case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM: + case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM: + case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM: + case VK_FORMAT_R10X6_UNORM_PACK16: + case VK_FORMAT_R10X6G10X6_UNORM_2PACK16: + case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: + case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: + case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: + case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16: + case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: + case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16: + case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16: + case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16: + case VK_FORMAT_R12X4_UNORM_PACK16: + case VK_FORMAT_R12X4G12X4_UNORM_2PACK16: + case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: + case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: + case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: + case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16: + case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16: + case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16: + case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16: + case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16: + case VK_FORMAT_G16B16G16R16_422_UNORM: + case VK_FORMAT_B16G16R16G16_422_UNORM: + case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM: + case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM: + case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM: + case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM: + case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM: + case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: + case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: + case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: + case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: + case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT: + case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT: + case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: + case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT: + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<VkPointClippingBehavior> { + static bool exist(uint32_t e) { + switch (e) { + case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES: + case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY: + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<VkExternalFenceHandleTypeFlagBits> { + static bool exist(uint32_t e) { + switch (e) { + case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT: + case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT: + case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT: + case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT: + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<VkExternalSemaphoreHandleTypeFlagBits> { + static bool exist(uint32_t e) { + switch (e) { + case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT: + case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT: + case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT: + case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT: + case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT: + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<VkDriverIdKHR> { + static bool exist(uint32_t e) { + switch (e) { + case VK_DRIVER_ID_AMD_PROPRIETARY: + case VK_DRIVER_ID_AMD_OPEN_SOURCE: + case VK_DRIVER_ID_MESA_RADV: + case VK_DRIVER_ID_NVIDIA_PROPRIETARY: + case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: + case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: + case VK_DRIVER_ID_IMAGINATION_PROPRIETARY: + case VK_DRIVER_ID_QUALCOMM_PROPRIETARY: + case VK_DRIVER_ID_ARM_PROPRIETARY: + case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: + case VK_DRIVER_ID_GGP_PROPRIETARY: + case VK_DRIVER_ID_BROADCOM_PROPRIETARY: + case VK_DRIVER_ID_MESA_LLVMPIPE: + case VK_DRIVER_ID_MOLTENVK: + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<VkShaderFloatControlsIndependence> { + static bool exist(uint32_t e) { + switch (e) { + case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY: + case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL: + case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE: + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<VkPipelineRobustnessBufferBehavior> { + static bool exist(uint32_t e) { + switch (e) { + case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT: + case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED: + case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS: + case VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2: + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<VkPipelineRobustnessImageBehavior> { + static bool exist(uint32_t e) { + switch (e) { + case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DEVICE_DEFAULT: + case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DISABLED: + case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS: + case VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS_2: + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<VkImageLayout> { + static bool exist(uint32_t e) { + switch (e) { + case VK_IMAGE_LAYOUT_UNDEFINED: + case VK_IMAGE_LAYOUT_GENERAL: + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + case VK_IMAGE_LAYOUT_PREINITIALIZED: + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: + case VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR: + case VK_IMAGE_LAYOUT_VIDEO_DECODE_SRC_KHR: + case VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR: + case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR: + case VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT: + case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR: +#ifdef VK_ENABLE_BETA_EXTENSIONS + case VK_IMAGE_LAYOUT_VIDEO_ENCODE_DST_KHR: +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + case VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR: +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + case VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR: +#endif + case VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT: + return true; + } + return false; + } +}; + +// VkSparseImageFormatProperties + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkExtent3D* extents) { + return + visitor->Visit("width", &extents->width) && + visitor->Visit("height", &extents->height) && + visitor->Visit("depth", &extents->depth); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkConformanceVersionKHR* version) { + return visitor->Visit("major", &version->major) && + visitor->Visit("minor", &version->minor) && + visitor->Visit("subminor", &version->subminor) && + visitor->Visit("patch", &version->patch); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkMemoryType* type) { + return + visitor->Visit("propertyFlags", &type->propertyFlags) && + visitor->Visit("heapIndex", &type->heapIndex); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkMemoryHeap* heap) { + return + visitor->Visit("size", &heap->size) && + visitor->Visit("flags", &heap->flags); +}\n\n""") + + f.write(f"{generate_core_template()}\n\n{generate_extension_struct_template()}\n\n") + f.write(generate_struct_template(VK.ALL_STRUCTS)) + + f.write("""\ +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkExternalFenceProperties* properties) { + return visitor->Visit("exportFromImportedHandleTypes", + &properties->exportFromImportedHandleTypes) && + visitor->Visit("compatibleHandleTypes", + &properties->compatibleHandleTypes) && + visitor->Visit("externalFenceFeatures", + &properties->externalFenceFeatures); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkExternalSemaphoreProperties* properties) { + return visitor->Visit("exportFromImportedHandleTypes", + &properties->exportFromImportedHandleTypes) && + visitor->Visit("compatibleHandleTypes", + &properties->compatibleHandleTypes) && + visitor->Visit("externalSemaphoreFeatures", + &properties->externalSemaphoreFeatures); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonLayer* layer) { + return visitor->Visit("properties", &layer->properties) && + visitor->Visit("extensions", &layer->extensions); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonDeviceGroup* device_group) { + return visitor->Visit("devices", &device_group->device_inds) && + visitor->Visit("subsetAllocation", + &device_group->properties.subsetAllocation); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { + bool ret = true; + switch (device->properties.apiVersion ^ + VK_API_VERSION_PATCH(device->properties.apiVersion)) { + case VK_API_VERSION_1_4: + ret &= visitor->Visit("core14", &device->core14); + FALLTHROUGH_INTENDED; + case VK_API_VERSION_1_3: + ret &= visitor->Visit("core13", &device->core13); + FALLTHROUGH_INTENDED; + case VK_API_VERSION_1_2: + ret &= visitor->Visit("core11", &device->core11); + ret &= visitor->Visit("core12", &device->core12); + FALLTHROUGH_INTENDED; + case VK_API_VERSION_1_1: + ret &=\n""") + + emit_struct_visits_by_vk_version(f, "VK_VERSION_1_1") + + f.write("""\ + visitor->Visit("externalFenceProperties", + &device->external_fence_properties) && + visitor->Visit("externalSemaphoreProperties", + &device->external_semaphore_properties); + FALLTHROUGH_INTENDED; + case VK_API_VERSION_1_0: + ret &=\n""") + + emit_struct_visits_by_vk_version(f, "VK_VERSION_1_0") + + f.write("""\ + visitor->Visit("queues", &device->queues) && + visitor->Visit("extensions", &device->extensions) && + visitor->Visit("layers", &device->layers) && + visitor->Visit("formats", &device->formats);\n\n""") + + for extension_name, _ in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items(): + struct_var = get_vkjson_struct_variable_name(extension_name) + f.write(f" if (device->{struct_var}.reported) {{\n") + f.write(f" ret &= visitor->Visit(\"{extension_name}\", &device->{struct_var});\n") + f.write(" }\n") + + f.write("""\ + } return ret; } + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) { + bool ret = true; + switch (instance->api_version ^ VK_API_VERSION_PATCH(instance->api_version)) { + case VK_API_VERSION_1_4: + FALLTHROUGH_INTENDED; + case VK_API_VERSION_1_3: + FALLTHROUGH_INTENDED; + case VK_API_VERSION_1_2: + ret &= visitor->Visit("apiVersion", &instance->api_version); + FALLTHROUGH_INTENDED; + case VK_API_VERSION_1_1: + ret &= visitor->Visit("deviceGroups", &instance->device_groups); + FALLTHROUGH_INTENDED; + case VK_API_VERSION_1_0: + char depString[] = + "vkjson is deprecated, and will be replaced in a future release"; + ret &= visitor->Visit("layers", &instance->layers) && + visitor->Visit("extensions", &instance->extensions) && + visitor->Visit("devices", &instance->devices) && + visitor->Visit("_comment", &depString); + } + return ret; +} + +template <typename T> +using EnableForArithmetic = + typename std::enable_if<std::is_arithmetic<T>::value, void>::type; + +template <typename T> +using EnableForStruct = + typename std::enable_if<std::is_class<T>::value, void>::type; + +template <typename T> +using EnableForEnum = + typename std::enable_if<std::is_enum<T>::value, void>::type; + +template <typename T, typename = EnableForStruct<T>, typename = void> +Json::Value ToJsonValue(const T& value); + +template <typename T, typename = EnableForArithmetic<T>> +inline Json::Value ToJsonValue(const T& value) { + return Json::Value( + std::clamp(static_cast<double>(value), SAFE_DOUBLE_MIN, SAFE_DOUBLE_MAX)); +} + +inline Json::Value ToJsonValue(const uint64_t& value) { + char string[19] = {0}; // "0x" + 16 digits + terminal \\0 + snprintf(string, sizeof(string), "0x%016" PRIx64, value); + return Json::Value(string); +} + +template <typename T, typename = EnableForEnum<T>, typename = void, + typename = void> +inline Json::Value ToJsonValue(const T& value) { + return Json::Value(static_cast<double>(value)); +} + +template <typename T> +inline Json::Value ArrayToJsonValue(uint32_t count, const T* values) { + Json::Value array(Json::arrayValue); + for (unsigned int i = 0; i < count; ++i) array.append(ToJsonValue(values[i])); + return array; +} + +template <typename T, size_t N> +inline Json::Value ToJsonValue(const T (&value)[N]) { + return ArrayToJsonValue(N, value); +} + +template <size_t N> +inline Json::Value ToJsonValue(const char (&value)[N]) { + assert(strlen(value) < N); + return Json::Value(value); +} + +template <typename T> +inline Json::Value ToJsonValue(const std::vector<T>& value) { + assert(value.size() <= std::numeric_limits<uint32_t>::max()); + return ArrayToJsonValue(static_cast<uint32_t>(value.size()), value.data()); +} + +template <typename F, typename S> +inline Json::Value ToJsonValue(const std::pair<F, S>& value) { + Json::Value array(Json::arrayValue); + array.append(ToJsonValue(value.first)); + array.append(ToJsonValue(value.second)); + return array; +} + +template <typename F, typename S> +inline Json::Value ToJsonValue(const std::map<F, S>& value) { + Json::Value array(Json::arrayValue); + for (auto& kv : value) array.append(ToJsonValue(kv)); + return array; +} + +class JsonWriterVisitor { + public: + JsonWriterVisitor() : object_(Json::objectValue) {} + + ~JsonWriterVisitor() {} + + template <typename T> bool Visit(const char* key, const T* value) { + object_[key] = ToJsonValue(*value); + return true; + } + + template <typename T, uint32_t N> + bool VisitArray(const char* key, uint32_t count, const T (*value)[N]) { + assert(count <= N); + object_[key] = ArrayToJsonValue(count, *value); + return true; + } + + template <typename T> + bool VisitArray(const char* key, uint32_t count, const T *value) { + object_[key] = ArrayToJsonValue(count, *value); + return true; + } + + Json::Value get_object() const { return object_; } + + private: + Json::Value object_; +}; + +template <typename Visitor, typename T> +inline void VisitForWrite(Visitor* visitor, const T& t) { + Iterate(visitor, const_cast<T*>(&t)); +} + +template <typename T, typename /*= EnableForStruct<T>*/, typename /*= void*/> +Json::Value ToJsonValue(const T& value) { + JsonWriterVisitor visitor; + VisitForWrite(&visitor, value); + return visitor.get_object(); +} + +template <typename T, typename = EnableForStruct<T>> +bool AsValue(Json::Value* json_value, T* t); + +inline bool AsValue(Json::Value* json_value, int32_t* value) { + if (json_value->type() != Json::realValue) return false; + double d = json_value->asDouble(); + if (!IsIntegral(d) || + d < static_cast<double>(std::numeric_limits<int32_t>::min()) || + d > static_cast<double>(std::numeric_limits<int32_t>::max())) + return false; + *value = static_cast<int32_t>(d); + return true; +} + +inline bool AsValue(Json::Value* json_value, uint64_t* value) { + if (json_value->type() != Json::stringValue) return false; + int result = + std::sscanf(json_value->asString().c_str(), "0x%016" PRIx64, value); + return result == 1; +} + +inline bool AsValue(Json::Value* json_value, uint32_t* value) { + if (json_value->type() != Json::realValue) return false; + double d = json_value->asDouble(); + if (!IsIntegral(d) || d < 0.0 || + d > static_cast<double>(std::numeric_limits<uint32_t>::max())) + return false; + *value = static_cast<uint32_t>(d); + return true; +} + +inline bool AsValue(Json::Value* json_value, uint8_t* value) { + uint32_t value32 = 0; + AsValue(json_value, &value32); + if (value32 > std::numeric_limits<uint8_t>::max()) + return false; + *value = static_cast<uint8_t>(value32); + return true; +} + +inline bool AsValue(Json::Value* json_value, float* value) { + if (json_value->type() != Json::realValue) return false; + *value = static_cast<float>(json_value->asDouble()); + return true; +} + +inline bool AsValue(Json::Value* json_value, VkImageLayout* t) { + uint32_t value = 0; + if (!AsValue(json_value, &value)) + return false; + if (!EnumTraits<VkImageLayout>::exist(value)) return false; + *t = static_cast<VkImageLayout>(value); + return true; +} + +template <typename T> +inline bool AsArray(Json::Value* json_value, uint32_t count, T* values) { + if (json_value->type() != Json::arrayValue || json_value->size() != count) + return false; + for (uint32_t i = 0; i < count; ++i) { + if (!AsValue(&(*json_value)[i], values + i)) return false; + } + return true; +} + +template <typename T, size_t N> +inline bool AsValue(Json::Value* json_value, T (*value)[N]) { + return AsArray(json_value, N, *value); +} + +template <size_t N> +inline bool AsValue(Json::Value* json_value, char (*value)[N]) { + if (json_value->type() != Json::stringValue) return false; + size_t len = json_value->asString().length(); + if (len >= N) + return false; + memcpy(*value, json_value->asString().c_str(), len); + memset(*value + len, 0, N-len); + return true; +} + +template <typename T, typename = EnableForEnum<T>, typename = void> +inline bool AsValue(Json::Value* json_value, T* t) { + uint32_t value = 0; + if (!AsValue(json_value, &value)) + return false; + if (!EnumTraits<T>::exist(value)) return false; + *t = static_cast<T>(value); + return true; +} + +template <typename T> +inline bool AsValue(Json::Value* json_value, std::vector<T>* value) { + if (json_value->type() != Json::arrayValue) return false; + int size = json_value->size(); + value->resize(size); + return AsArray(json_value, size, value->data()); +} + +template <typename F, typename S> +inline bool AsValue(Json::Value* json_value, std::pair<F, S>* value) { + if (json_value->type() != Json::arrayValue || json_value->size() != 2) + return false; + return AsValue(&(*json_value)[0], &value->first) && + AsValue(&(*json_value)[1], &value->second); +} + +template <typename F, typename S> +inline bool AsValue(Json::Value* json_value, std::map<F, S>* value) { + if (json_value->type() != Json::arrayValue) return false; + int size = json_value->size(); + for (int i = 0; i < size; ++i) { + std::pair<F, S> elem; + if (!AsValue(&(*json_value)[i], &elem)) return false; + if (!value->insert(elem).second) + return false; + } + return true; +} + +template <typename T> +bool ReadValue(Json::Value* object, const char* key, T* value, + std::string* errors) { + Json::Value json_value = (*object)[key]; + if (!json_value) { + if (errors) + *errors = std::string(key) + " missing."; + return false; + } + if (AsValue(&json_value, value)) return true; + if (errors) + *errors = std::string("Wrong type for ") + std::string(key) + "."; + return false; +} + +template <typename Visitor, typename T> +inline bool VisitForRead(Visitor* visitor, T* t) { + return Iterate(visitor, t); +} + +class JsonReaderVisitor { + public: + JsonReaderVisitor(Json::Value* object, std::string* errors) + : object_(object), errors_(errors) {} + + template <typename T> bool Visit(const char* key, T* value) const { + return ReadValue(object_, key, value, errors_); + } + + template <typename T, uint32_t N> + bool VisitArray(const char* key, uint32_t count, T (*value)[N]) { + if (count > N) + return false; + Json::Value json_value = (*object_)[key]; + if (!json_value) { + if (errors_) + *errors_ = std::string(key) + " missing."; + return false; + } + if (AsArray(&json_value, count, *value)) return true; + if (errors_) + *errors_ = std::string("Wrong type for ") + std::string(key) + "."; + return false; + } + + template <typename T> + bool VisitArray(const char* key, uint32_t count, T *value) { + Json::Value json_value = (*object_)[key]; + if (!json_value) { + if (errors_) + *errors_ = std::string(key) + " missing."; + return false; + } + if (AsArray(&json_value, count, *value)) return true; + if (errors_) + *errors_ = std::string("Wrong type for ") + std::string(key) + "."; + return false; + } + + + private: + Json::Value* object_; + std::string* errors_; +}; + +template <typename T, typename /*= EnableForStruct<T>*/> +bool AsValue(Json::Value* json_value, T* t) { + if (json_value->type() != Json::objectValue) return false; + JsonReaderVisitor visitor(json_value, nullptr); + return VisitForRead(&visitor, t); +} + + +template <typename T> std::string VkTypeToJson(const T& t) { + JsonWriterVisitor visitor; + VisitForWrite(&visitor, t); + return visitor.get_object().toStyledString(); +} + +template <typename T> bool VkTypeFromJson(const std::string& json, + T* t, + std::string* errors) { + *t = T(); + Json::Value object(Json::objectValue); + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); + if (!reader->parse(json.data(), json.data() + json.size(), &object, errors)) { + return false; + } + return AsValue(&object, t); +} + +} // anonymous namespace + +std::string VkJsonInstanceToJson(const VkJsonInstance& instance) { + return VkTypeToJson(instance); +} + +bool VkJsonInstanceFromJson(const std::string& json, + VkJsonInstance* instance, + std::string* errors) { + return VkTypeFromJson(json, instance, errors); +} + +std::string VkJsonDeviceToJson(const VkJsonDevice& device) { + return VkTypeToJson(device); +} + +bool VkJsonDeviceFromJson(const std::string& json, + VkJsonDevice* device, + std::string* errors) { + return VkTypeFromJson(json, device, errors); +}; + +std::string VkJsonImageFormatPropertiesToJson( + const VkImageFormatProperties& properties) { + return VkTypeToJson(properties); +} + +bool VkJsonImageFormatPropertiesFromJson(const std::string& json, + VkImageFormatProperties* properties, + std::string* errors) { + return VkTypeFromJson(json, properties, errors); +}; +""") + f.close() + gencom.run_clang_format(genfile) + + +def generate_vk_core_structs_init_code(version): + """Generates code to initialize properties and features + for structs based on its vulkan API version dependency. + """ + properties_code, features_code = [], [] + + for item in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].get(version, []): + for struct_name, struct_type in item.items(): + version_lower = version.lower() + + if "Properties" in struct_name: + properties_code.extend([ + f"device.{version_lower}.properties.sType = {struct_type};", + f"device.{version_lower}.properties.pNext = properties.pNext;", + f"properties.pNext = &device.{version_lower}.properties;\n\n" + ]) + + elif "Features" in struct_name: + features_code.extend([ + f"device.{version_lower}.features.sType = {struct_type};", + f"device.{version_lower}.features.pNext = features.pNext;", + f"features.pNext = &device.{version_lower}.features;\n\n" + ]) + + return "\n".join(properties_code), "\n".join(features_code) + + +def generate_vk_extension_structs_init_code(mapping, struct_category, next_pointer): + """Generates Vulkan struct initialization code for given struct category (Properties/Features) + based on its extension dependency. + """ + generated_code = [] + + for extension, struct_mappings in mapping.items(): + struct_var_name = get_vkjson_struct_variable_name(extension) + extension_code = [ + f" if (HasExtension(\"{extension}\", device.extensions)) {{", + f" device.{struct_var_name}.reported = true;" + ] + + struct_count = 0 + for struct_mapping in struct_mappings: + for struct_name, struct_type in struct_mapping.items(): + if struct_category in struct_name: + struct_count += 1 + struct_instance = get_struct_name(struct_name) + extension_code.extend([ + f" device.{struct_var_name}.{struct_instance}.sType = {struct_type};", + f" device.{struct_var_name}.{struct_instance}.pNext = {next_pointer}.pNext;", + f" {next_pointer}.pNext = &device.{struct_var_name}.{struct_instance};" + ]) + + extension_code.append(" }\n") + + if struct_count > 0: + generated_code.extend(extension_code) + + return "\n".join(generated_code) + + +def generate_vk_version_structs_initialization(version_data, struct_type_keyword, next_pointer): + """Generates Vulkan struct initialization code for given struct category (Properties/Features) + of vulkan api version s. + """ + struct_initialization_code = [] + + for struct_mapping in version_data: + for struct_name, struct_type in struct_mapping.items(): + if struct_type_keyword in struct_name: + struct_variable = get_struct_name(struct_name) + struct_initialization_code.extend([ + f"device.{struct_variable}.sType = {struct_type};", + f"device.{struct_variable}.pNext = {next_pointer}.pNext;", + f"{next_pointer}.pNext = &device.{struct_variable};\n" + ]) + + return "\n".join(struct_initialization_code) + + +def gen_instance_cc(): + """Generates vkjson_instance.cc file. + """ + genfile = os.path.join(os.path.dirname(__file__), + "..", "vkjson", "vkjson_instance.cc") + + with open(genfile, "w") as f: + f.write(get_copyright_warnings()) + f.write("\n") + + f.write("""\ +#ifndef VK_PROTOTYPES +#define VK_PROTOTYPES +#endif + +#include "vkjson.h" + +#include <algorithm> +#include <utility> + +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ +namespace { + +bool EnumerateExtensions(const char* layer_name, + std::vector<VkExtensionProperties>* extensions) { + VkResult result; + uint32_t count = 0; + result = vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr); + if (result != VK_SUCCESS) + return false; + extensions->resize(count); + result = vkEnumerateInstanceExtensionProperties(layer_name, &count, + extensions->data()); + if (result != VK_SUCCESS) + return false; + return true; +} + +bool HasExtension(const char* extension_name, + const std::vector<VkExtensionProperties>& extensions) { + return std::find_if(extensions.cbegin(), extensions.cend(), + [extension_name](const VkExtensionProperties& extension) { + return strcmp(extension.extensionName, + extension_name) == 0; + }) != extensions.cend(); +} +} // anonymous namespace + +VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { + VkJsonDevice device; + + uint32_t extension_count = 0; + vkEnumerateDeviceExtensionProperties(physical_device, nullptr, + &extension_count, nullptr); + if (extension_count > 0) { + device.extensions.resize(extension_count); + vkEnumerateDeviceExtensionProperties( + physical_device, nullptr, &extension_count, device.extensions.data()); + } + + uint32_t layer_count = 0; + vkEnumerateDeviceLayerProperties(physical_device, &layer_count, nullptr); + if (layer_count > 0) { + device.layers.resize(layer_count); + vkEnumerateDeviceLayerProperties(physical_device, &layer_count, + device.layers.data()); + } + + VkPhysicalDeviceProperties2 properties = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + nullptr, + {}, + };\n\n""") + + cc_code_properties = generate_vk_extension_structs_init_code( + VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"], "Properties", "properties" + ) + f.write(f'{cc_code_properties}\n') + + f.write("""\ + + vkGetPhysicalDeviceProperties2(physical_device, &properties); + device.properties = properties.properties; + + VkPhysicalDeviceFeatures2 features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + nullptr, + {}, + };\n\n""") + + cc_code_features = generate_vk_extension_structs_init_code( + VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"], "Features", "features" + ) + f.write(f'{cc_code_features}\n') + + f.write("""\ + + vkGetPhysicalDeviceFeatures2(physical_device, &features); + device.features = features.features; + + vkGetPhysicalDeviceMemoryProperties(physical_device, &device.memory); + + uint32_t queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, + nullptr); + if (queue_family_count > 0) { + device.queues.resize(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties( + physical_device, &queue_family_count, device.queues.data()); + } + + VkFormatProperties format_properties = {}; + for (VkFormat format = VK_FORMAT_R4G4_UNORM_PACK8; + // TODO(http://b/171403054): avoid hard-coding last value in the + // contiguous range + format <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK; + format = static_cast<VkFormat>(format + 1)) { + vkGetPhysicalDeviceFormatProperties(physical_device, format, + &format_properties); + if (format_properties.linearTilingFeatures || + format_properties.optimalTilingFeatures || + format_properties.bufferFeatures) { + device.formats.insert(std::make_pair(format, format_properties)); + } + } + + if (device.properties.apiVersion >= VK_API_VERSION_1_1) { + for (VkFormat format = VK_FORMAT_G8B8G8R8_422_UNORM; + // TODO(http://b/171403054): avoid hard-coding last value in the + // contiguous range + format <= VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM; + format = static_cast<VkFormat>(format + 1)) { + vkGetPhysicalDeviceFormatProperties(physical_device, format, + &format_properties); + if (format_properties.linearTilingFeatures || + format_properties.optimalTilingFeatures || + format_properties.bufferFeatures) { + device.formats.insert(std::make_pair(format, format_properties)); + } + }\n\n""") + + # Vulkan version data for VK_VERSION_1_1 + vk_version_data = VK.VULKAN_VERSIONS_AND_STRUCTS_MAPPING["VK_VERSION_1_1"] + f.write(generate_vk_version_structs_initialization(vk_version_data, "Properties", "properties") + "\n") + + f.write("""\ + vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n""") + + features_initialization_code = generate_vk_version_structs_initialization(vk_version_data, "Features", "features") + f.write(features_initialization_code) + + f.write("""\ + + vkGetPhysicalDeviceFeatures2(physical_device, &features); + + VkPhysicalDeviceExternalFenceInfo external_fence_info = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr, + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT}; + VkExternalFenceProperties external_fence_properties = {}; + + for (VkExternalFenceHandleTypeFlagBits handle_type = + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; + handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; + handle_type = + static_cast<VkExternalFenceHandleTypeFlagBits>(handle_type << 1)) { + external_fence_info.handleType = handle_type; + vkGetPhysicalDeviceExternalFenceProperties( + physical_device, &external_fence_info, &external_fence_properties); + if (external_fence_properties.exportFromImportedHandleTypes || + external_fence_properties.compatibleHandleTypes || + external_fence_properties.externalFenceFeatures) { + device.external_fence_properties.insert( + std::make_pair(handle_type, external_fence_properties)); + } + } + + VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT}; + VkExternalSemaphoreProperties external_semaphore_properties = {}; + + for (VkExternalSemaphoreHandleTypeFlagBits handle_type = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; + handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>( + handle_type << 1)) { + external_semaphore_info.handleType = handle_type; + vkGetPhysicalDeviceExternalSemaphoreProperties( + physical_device, &external_semaphore_info, + &external_semaphore_properties); + if (external_semaphore_properties.exportFromImportedHandleTypes || + external_semaphore_properties.compatibleHandleTypes || + external_semaphore_properties.externalSemaphoreFeatures) { + device.external_semaphore_properties.insert( + std::make_pair(handle_type, external_semaphore_properties)); + } + } + } + + if (device.properties.apiVersion >= VK_API_VERSION_1_2) {\n""") + + cc_code_properties_11, cc_code_features_11 = generate_vk_core_structs_init_code("Core11") + cc_code_properties_12, cc_code_features_12 = generate_vk_core_structs_init_code("Core12") + cc_code_properties_13, cc_code_features_13 = generate_vk_core_structs_init_code("Core13") + cc_code_properties_14, cc_code_features_14 = generate_vk_core_structs_init_code("Core14") + + f.write(cc_code_properties_11) + f.write(cc_code_properties_12) + f.write(f"vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n") + f.write(cc_code_features_11) + f.write(cc_code_features_12) + f.write(f"vkGetPhysicalDeviceFeatures2(physical_device, &features);\n\n") + f.write("""\ + } + + if (device.properties.apiVersion >= VK_API_VERSION_1_3) {\n""") + f.write(cc_code_properties_13) + f.write(f"vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n") + f.write(cc_code_features_13) + f.write(f"vkGetPhysicalDeviceFeatures2(physical_device, &features);\n\n") + f.write("""\ + } + + if (device.properties.apiVersion >= VK_API_VERSION_1_4) {\n""") + f.write(cc_code_properties_14) + f.write(f"vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n") + f.write(cc_code_features_14) + f.write(f"vkGetPhysicalDeviceFeatures2(physical_device, &features);\n\n") + f.write("""\ + } + + return device; +} + +VkJsonInstance VkJsonGetInstance() { + VkJsonInstance instance; + VkResult result; + uint32_t count; + + count = 0; + result = vkEnumerateInstanceLayerProperties(&count, nullptr); + if (result != VK_SUCCESS) + return VkJsonInstance(); + if (count > 0) { + std::vector<VkLayerProperties> layers(count); + result = vkEnumerateInstanceLayerProperties(&count, layers.data()); + if (result != VK_SUCCESS) + return VkJsonInstance(); + instance.layers.reserve(count); + for (auto& layer : layers) { + instance.layers.push_back(VkJsonLayer{layer, std::vector<VkExtensionProperties>()}); + if (!EnumerateExtensions(layer.layerName, + &instance.layers.back().extensions)) + return VkJsonInstance(); + } + } + + if (!EnumerateExtensions(nullptr, &instance.extensions)) + return VkJsonInstance(); + + const VkApplicationInfo app_info = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, + nullptr, + "vkjson_info", + 1, + "", + 0, + VK_API_VERSION_1_1, + }; + VkInstanceCreateInfo instance_info = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + nullptr, + 0, + &app_info, + 0, + nullptr, + 0, + nullptr, + }; + VkInstance vkinstance; + result = vkCreateInstance(&instance_info, nullptr, &vkinstance); + if (result != VK_SUCCESS) + return VkJsonInstance(); + + count = 0; + result = vkEnumeratePhysicalDevices(vkinstance, &count, nullptr); + if (result != VK_SUCCESS) { + vkDestroyInstance(vkinstance, nullptr); + return VkJsonInstance(); + } + + std::vector<VkPhysicalDevice> devices(count, VK_NULL_HANDLE); + result = vkEnumeratePhysicalDevices(vkinstance, &count, devices.data()); + if (result != VK_SUCCESS) { + vkDestroyInstance(vkinstance, nullptr); + return VkJsonInstance(); + } + + std::map<VkPhysicalDevice, uint32_t> device_map; + const uint32_t sz = devices.size(); + instance.devices.reserve(sz); + for (uint32_t i = 0; i < sz; ++i) { + device_map.insert(std::make_pair(devices[i], i)); + instance.devices.emplace_back(VkJsonGetDevice(devices[i])); + } + + result = vkEnumerateInstanceVersion(&instance.api_version); + if (result != VK_SUCCESS) { + vkDestroyInstance(vkinstance, nullptr); + return VkJsonInstance(); + } + + count = 0; + result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, nullptr); + if (result != VK_SUCCESS) { + vkDestroyInstance(vkinstance, nullptr); + return VkJsonInstance(); + } + + VkJsonDeviceGroup device_group; + std::vector<VkPhysicalDeviceGroupProperties> group_properties; + group_properties.resize(count); + for (auto& properties : group_properties) { + properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES; + properties.pNext = nullptr; + } + result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, + group_properties.data()); + if (result != VK_SUCCESS) { + vkDestroyInstance(vkinstance, nullptr); + return VkJsonInstance(); + } + for (auto properties : group_properties) { + device_group.properties = properties; + for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) { + device_group.device_inds.push_back( + device_map[properties.physicalDevices[i]]); + } + instance.device_groups.push_back(device_group); + } + + vkDestroyInstance(vkinstance, nullptr); + return instance; +} + +\n""") + + f.close() + gencom.run_clang_format(genfile)
\ No newline at end of file diff --git a/vulkan/tests/Android.bp b/vulkan/tests/Android.bp new file mode 100644 index 0000000000..db218c18ee --- /dev/null +++ b/vulkan/tests/Android.bp @@ -0,0 +1,57 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_test { + name: "libvulkan_test", + test_suites: ["general-tests"], + + srcs: [ + "libvulkan_test.cpp", + ], + + strip: { + none: true, + }, + + cflags: [ + "-DVK_USE_PLATFORM_ANDROID_KHR", + "-Wall", + "-Werror", + ], + + header_libs: [ + "hwvulkan_headers", + "libvulkanprivate_headers-testing", + "vulkan_headers", + ], + + cppflags: [ + "-Wno-c++98-compat-pedantic", + "-Wno-c99-extensions", + "-Wno-exit-time-destructors", + "-Wno-float-equal", + "-Wno-global-constructors", + "-Wno-zero-length-array", + ], + + shared_libs: [ + "libbase", + "libgraphicsenv", + "liblog", + "libmediandk", + "libvulkan", + ], + + static_libs: [ + "libgmock", + "libgtest", + "liblog", + ], + +} diff --git a/vulkan/tests/README.md b/vulkan/tests/README.md new file mode 100644 index 0000000000..3c9b66cee5 --- /dev/null +++ b/vulkan/tests/README.md @@ -0,0 +1,24 @@ +#libvulkan_test + +This binary contains the unit tests for testing libvulkan (The Vulkan Loader). + +These tests rely on the underlying GPU driver to be able to successfully create a valid +swapchain. These tests are design to run on an Android emulator to give us a consistent GPU +driver to test against. YMMV when running this on a physical device with an arbitrary GPU +driver. + +To run these tests run: +``` +atest libvulkan_test +``` + +If using an acloud device the full command list for the root of a freshly cloned repo would be: +``` +source build/envsetup.sh +lunch aosp_cf_x86_64_phone-trunk_staging-eng +m +acloud create --local-image +atest libvulkan_test +``` + + diff --git a/vulkan/tests/libvulkan_test.cpp b/vulkan/tests/libvulkan_test.cpp new file mode 100644 index 0000000000..7e4bfd8e80 --- /dev/null +++ b/vulkan/tests/libvulkan_test.cpp @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/log.h> +#include <driver.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <media/NdkImageReader.h> +#include <vulkan/vulkan.h> + +#define LOGI(...) \ + __android_log_print(ANDROID_LOG_INFO, "libvulkan_test", __VA_ARGS__) +#define LOGE(...) \ + __android_log_print(ANDROID_LOG_ERROR, "libvulkan_test", __VA_ARGS__) + +#define VK_CHECK(result) ASSERT_EQ(VK_SUCCESS, result) + +namespace android { + +namespace libvulkantest { + +class AImageReaderVulkanSwapchainTest : public ::testing::Test { + public: + AImageReaderVulkanSwapchainTest() {} + + AImageReader* mReader = nullptr; + ANativeWindow* mWindow = nullptr; + VkInstance mVkInstance = VK_NULL_HANDLE; + VkPhysicalDevice mPhysicalDev = VK_NULL_HANDLE; + VkDevice mDevice = VK_NULL_HANDLE; + VkSurfaceKHR mSurface = VK_NULL_HANDLE; + VkQueue mPresentQueue = VK_NULL_HANDLE; + uint32_t mPresentQueueFamily = UINT32_MAX; + VkSwapchainKHR mSwapchain = VK_NULL_HANDLE; + + void SetUp() override {} + + void TearDown() override {} + + // ------------------------------------------------------ + // Helper methods + // ------------------------------------------------------ + + void createVulkanInstance(std::vector<const char*>& layers) { + const char* extensions[] = { + VK_KHR_SURFACE_EXTENSION_NAME, + VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, + VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, + }; + + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "AImageReader Vulkan Swapchain Test"; + appInfo.applicationVersion = 1; + appInfo.pEngineName = "TestEngine"; + appInfo.engineVersion = 1; + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo instInfo{}; + instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instInfo.pApplicationInfo = &appInfo; + instInfo.enabledExtensionCount = + sizeof(extensions) / sizeof(extensions[0]); + instInfo.ppEnabledExtensionNames = extensions; + instInfo.enabledLayerCount = layers.size(); + instInfo.ppEnabledLayerNames = layers.data(); + VkResult res = vkCreateInstance(&instInfo, nullptr, &mVkInstance); + VK_CHECK(res); + LOGE("Vulkan instance created"); + } + + void createAImageReader(int width, int height, int format, int maxImages) { + media_status_t status = + AImageReader_new(width, height, format, maxImages, &mReader); + ASSERT_EQ(AMEDIA_OK, status) << "Failed to create AImageReader"; + ASSERT_NE(nullptr, mReader) << "AImageReader is null"; + + // Optionally set a listener + AImageReader_ImageListener listener{}; + listener.context = this; + listener.onImageAvailable = + &AImageReaderVulkanSwapchainTest::onImageAvailable; + AImageReader_setImageListener(mReader, &listener); + + LOGI("AImageReader created with %dx%d, format=%d", width, height, + format); + } + + void getANativeWindowFromReader() { + ASSERT_NE(nullptr, mReader); + + media_status_t status = AImageReader_getWindow(mReader, &mWindow); + ASSERT_EQ(AMEDIA_OK, status) + << "Failed to get ANativeWindow from AImageReader"; + ASSERT_NE(nullptr, mWindow) << "ANativeWindow is null"; + LOGI("ANativeWindow obtained from AImageReader"); + } + + void createVulkanSurface() { + ASSERT_NE((VkInstance)VK_NULL_HANDLE, mVkInstance); + ASSERT_NE((ANativeWindow*)nullptr, mWindow); + + VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo{}; + surfaceCreateInfo.sType = + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.window = mWindow; + + VkResult res = vkCreateAndroidSurfaceKHR( + mVkInstance, &surfaceCreateInfo, nullptr, &mSurface); + VK_CHECK(res); + LOGI("Vulkan surface created from ANativeWindow"); + } + + void pickPhysicalDeviceAndQueueFamily() { + ASSERT_NE((VkInstance)VK_NULL_HANDLE, mVkInstance); + + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(mVkInstance, &deviceCount, nullptr); + ASSERT_GT(deviceCount, 0U) << "No Vulkan physical devices found!"; + + std::vector<VkPhysicalDevice> devices(deviceCount); + vkEnumeratePhysicalDevices(mVkInstance, &deviceCount, devices.data()); + + for (auto& dev : devices) { + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(dev, &queueFamilyCount, + nullptr); + std::vector<VkQueueFamilyProperties> queueProps(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(dev, &queueFamilyCount, + queueProps.data()); + + for (uint32_t i = 0; i < queueFamilyCount; i++) { + VkBool32 support = VK_FALSE; + vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, mSurface, + &support); + if (support == VK_TRUE) { + // Found a queue family that can present + mPhysicalDev = dev; + mPresentQueueFamily = i; + + LOGI( + "Physical device found with queue family %u supporting " + "present", + i); + return; + } + } + } + + FAIL() + << "No physical device found that supports present to the surface!"; + } + + void createDeviceAndGetQueue(std::vector<const char*>& layers, + std::vector<const char*> inExtensions = {}) { + ASSERT_NE((void*)VK_NULL_HANDLE, mPhysicalDev); + ASSERT_NE(UINT32_MAX, mPresentQueueFamily); + + float queuePriority = 1.0f; + VkDeviceQueueCreateInfo queueInfo{}; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.queueFamilyIndex = mPresentQueueFamily; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = &queuePriority; + + VkDeviceCreateInfo deviceInfo{}; + deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceInfo.queueCreateInfoCount = 1; + deviceInfo.pQueueCreateInfos = &queueInfo; + deviceInfo.enabledLayerCount = layers.size(); + deviceInfo.ppEnabledLayerNames = layers.data(); + + std::vector<const char*> extensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + for (auto extension : inExtensions) { + extensions.push_back(extension); + } + deviceInfo.enabledExtensionCount = extensions.size(); + deviceInfo.ppEnabledExtensionNames = extensions.data(); + + VkResult res = + vkCreateDevice(mPhysicalDev, &deviceInfo, nullptr, &mDevice); + VK_CHECK(res); + LOGI("Logical device created"); + + vkGetDeviceQueue(mDevice, mPresentQueueFamily, 0, &mPresentQueue); + ASSERT_NE((VkQueue)VK_NULL_HANDLE, mPresentQueue); + LOGI("Acquired present-capable queue"); + } + + void createSwapchain() { + ASSERT_NE((VkDevice)VK_NULL_HANDLE, mDevice); + ASSERT_NE((VkSurfaceKHR)VK_NULL_HANDLE, mSurface); + + VkSurfaceCapabilitiesKHR surfaceCaps{}; + VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + mPhysicalDev, mSurface, &surfaceCaps)); + + uint32_t formatCount = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface, + &formatCount, nullptr); + ASSERT_GT(formatCount, 0U); + std::vector<VkSurfaceFormatKHR> formats(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface, + &formatCount, formats.data()); + + VkSurfaceFormatKHR chosenFormat = formats[0]; + LOGI("Chosen surface format: %d", chosenFormat.format); + + uint32_t presentModeCount = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDev, mSurface, + &presentModeCount, nullptr); + ASSERT_GT(presentModeCount, 0U); + std::vector<VkPresentModeKHR> presentModes(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR( + mPhysicalDev, mSurface, &presentModeCount, presentModes.data()); + + VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR; + for (auto mode : presentModes) { + if (mode == VK_PRESENT_MODE_FIFO_KHR) { + chosenPresentMode = mode; + break; + } + } + LOGI("Chosen present mode: %d", chosenPresentMode); + + VkExtent2D swapchainExtent{}; + if (surfaceCaps.currentExtent.width == 0xFFFFFFFF) { + swapchainExtent.width = 640; // fallback + swapchainExtent.height = 480; // fallback + } else { + swapchainExtent = surfaceCaps.currentExtent; + } + LOGI("Swapchain extent: %d x %d", swapchainExtent.width, + swapchainExtent.height); + + uint32_t desiredImageCount = surfaceCaps.minImageCount + 1; + if (surfaceCaps.maxImageCount > 0 && + desiredImageCount > surfaceCaps.maxImageCount) { + desiredImageCount = surfaceCaps.maxImageCount; + } + + VkSwapchainCreateInfoKHR swapchainInfo{}; + swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainInfo.surface = mSurface; + swapchainInfo.minImageCount = desiredImageCount; + swapchainInfo.imageFormat = chosenFormat.format; + swapchainInfo.imageColorSpace = chosenFormat.colorSpace; + swapchainInfo.imageExtent = swapchainExtent; + swapchainInfo.imageArrayLayers = 1; + swapchainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + swapchainInfo.preTransform = surfaceCaps.currentTransform; + swapchainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + swapchainInfo.presentMode = chosenPresentMode; + swapchainInfo.clipped = VK_TRUE; + swapchainInfo.oldSwapchain = VK_NULL_HANDLE; + + uint32_t queueFamilyIndices[] = {mPresentQueueFamily}; + swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainInfo.queueFamilyIndexCount = 1; + swapchainInfo.pQueueFamilyIndices = queueFamilyIndices; + + VkResult res = + vkCreateSwapchainKHR(mDevice, &swapchainInfo, nullptr, &mSwapchain); + if (res == VK_SUCCESS) { + LOGI("Swapchain created successfully"); + + uint32_t swapchainImageCount = 0; + vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount, + nullptr); + std::vector<VkImage> swapchainImages(swapchainImageCount); + vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount, + swapchainImages.data()); + + LOGI("Swapchain has %u images", swapchainImageCount); + } else { + LOGI("Swapchain creation failed"); + } + } + + // Image available callback (AImageReader) + static void onImageAvailable(void*, AImageReader* reader) { + LOGI("onImageAvailable callback triggered"); + AImage* image = nullptr; + media_status_t status = AImageReader_acquireLatestImage(reader, &image); + if (status != AMEDIA_OK || !image) { + LOGE("Failed to acquire latest image"); + return; + } + AImage_delete(image); + LOGI("Released acquired image"); + } + + void cleanUpSwapchainForTest() { + if (mSwapchain != VK_NULL_HANDLE) { + vkDestroySwapchainKHR(mDevice, mSwapchain, nullptr); + mSwapchain = VK_NULL_HANDLE; + } + if (mDevice != VK_NULL_HANDLE) { + vkDestroyDevice(mDevice, nullptr); + mDevice = VK_NULL_HANDLE; + } + if (mSurface != VK_NULL_HANDLE) { + vkDestroySurfaceKHR(mVkInstance, mSurface, nullptr); + mSurface = VK_NULL_HANDLE; + } + if (mVkInstance != VK_NULL_HANDLE) { + vkDestroyInstance(mVkInstance, nullptr); + mVkInstance = VK_NULL_HANDLE; + } + if (mReader) { + // AImageReader_delete(mReader); + mReader = nullptr; + } + // Note: The ANativeWindow from AImageReader is implicitly + // managed by the reader, so we don't explicitly delete it. + mWindow = nullptr; + } + + void buildSwapchianForTest(std::vector<const char*>& instanceLayers, + std::vector<const char*>& deviceLayers) { + createVulkanInstance(instanceLayers); + + // the "atest libvulkan_test" command will execute this test as a binary + // (not apk) on the device. Consequently we can't render to the screen + // and need to work around this by using AImageReader* + createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3); + getANativeWindowFromReader(); + createVulkanSurface(); + pickPhysicalDeviceAndQueueFamily(); + + createDeviceAndGetQueue(deviceLayers); + createSwapchain(); + } +}; + +TEST_F(AImageReaderVulkanSwapchainTest, TestHelperMethods) { + // Verify that the basic plumbing/helper functions of these tests is + // working. This doesn't directly test any of the layer code. It only + // verifies that we can successfully create a swapchain with an AImageReader + + std::vector<const char*> instanceLayers; + std::vector<const char*> deviceLayers; + buildSwapchianForTest(deviceLayers, instanceLayers); + + ASSERT_NE(mVkInstance, (VkInstance)VK_NULL_HANDLE); + ASSERT_NE(mPhysicalDev, (VkPhysicalDevice)VK_NULL_HANDLE); + ASSERT_NE(mDevice, (VkDevice)VK_NULL_HANDLE); + ASSERT_NE(mSurface, (VkSurfaceKHR)VK_NULL_HANDLE); + ASSERT_NE(mSwapchain, (VkSwapchainKHR)VK_NULL_HANDLE); + cleanUpSwapchainForTest(); +} + +// Passing state in these tests requires global state. Wrap each test in an +// anonymous namespace to prevent conflicting names. +namespace { + +VKAPI_ATTR VkResult VKAPI_CALL hookedGetPhysicalDeviceImageFormatProperties2KHR( + VkPhysicalDevice, + const VkPhysicalDeviceImageFormatInfo2*, + VkImageFormatProperties2*) { + return VK_ERROR_SURFACE_LOST_KHR; +} + +static PFN_vkGetSwapchainGrallocUsage2ANDROID + pfnNextGetSwapchainGrallocUsage2ANDROID = nullptr; + +static bool g_grallocCalled = false; + +VKAPI_ATTR VkResult VKAPI_CALL hookGetSwapchainGrallocUsage2ANDROID( + VkDevice device, + VkFormat format, + VkImageUsageFlags imageUsage, + VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, + uint64_t* grallocConsumerUsage, + uint64_t* grallocProducerUsage) { + g_grallocCalled = true; + if (pfnNextGetSwapchainGrallocUsage2ANDROID) { + return pfnNextGetSwapchainGrallocUsage2ANDROID( + device, format, imageUsage, swapchainImageUsage, + grallocConsumerUsage, grallocProducerUsage); + } + + return VK_ERROR_INITIALIZATION_FAILED; +} + +TEST_F(AImageReaderVulkanSwapchainTest, getProducerUsageFallbackTest1) { + // BUG: 379230826 + // Verify that getProducerUsage falls back to + // GetSwapchainGrallocUsage*ANDROID if GPDIFP2 fails + std::vector<const char*> instanceLayers = {}; + std::vector<const char*> deviceLayers = {}; + createVulkanInstance(instanceLayers); + + createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3); + getANativeWindowFromReader(); + createVulkanSurface(); + pickPhysicalDeviceAndQueueFamily(); + + createDeviceAndGetQueue(deviceLayers); + auto& pdev = vulkan::driver::GetData(mDevice).driver_physical_device; + auto& pdevDispatchTable = vulkan::driver::GetData(pdev).driver; + auto& deviceDispatchTable = vulkan::driver::GetData(mDevice).driver; + + ASSERT_NE(deviceDispatchTable.GetSwapchainGrallocUsage2ANDROID, nullptr); + + pdevDispatchTable.GetPhysicalDeviceImageFormatProperties2 = + hookedGetPhysicalDeviceImageFormatProperties2KHR; + deviceDispatchTable.GetSwapchainGrallocUsage2ANDROID = + hookGetSwapchainGrallocUsage2ANDROID; + + ASSERT_FALSE(g_grallocCalled); + + createSwapchain(); + + ASSERT_TRUE(g_grallocCalled); + + ASSERT_NE(mVkInstance, (VkInstance)VK_NULL_HANDLE); + ASSERT_NE(mPhysicalDev, (VkPhysicalDevice)VK_NULL_HANDLE); + ASSERT_NE(mDevice, (VkDevice)VK_NULL_HANDLE); + ASSERT_NE(mSurface, (VkSurfaceKHR)VK_NULL_HANDLE); + cleanUpSwapchainForTest(); +} + +} // namespace + +// Passing state in these tests requires global state. Wrap each test in an +// anonymous namespace to prevent conflicting names. +namespace { + +static bool g_returnNotSupportedOnce = true; + +VKAPI_ATTR VkResult VKAPI_CALL +Hook_GetPhysicalDeviceImageFormatProperties2_NotSupportedOnce( + VkPhysicalDevice /*physicalDevice*/, + const VkPhysicalDeviceImageFormatInfo2* /*pImageFormatInfo*/, + VkImageFormatProperties2* /*pImageFormatProperties*/) { + if (g_returnNotSupportedOnce) { + g_returnNotSupportedOnce = false; + return VK_ERROR_FORMAT_NOT_SUPPORTED; + } + return VK_SUCCESS; +} + +TEST_F(AImageReaderVulkanSwapchainTest, SurfaceFormats2KHR_IgnoreNotSupported) { + // BUG: 357903074 + // Verify that vkGetPhysicalDeviceSurfaceFormats2KHR properly + // ignores VK_ERROR_FORMAT_NOT_SUPPORTED and continues enumerating formats. + std::vector<const char*> instanceLayers; + createVulkanInstance(instanceLayers); + createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3); + getANativeWindowFromReader(); + createVulkanSurface(); + pickPhysicalDeviceAndQueueFamily(); + + auto& pdevDispatchTable = vulkan::driver::GetData(mPhysicalDev).driver; + pdevDispatchTable.GetPhysicalDeviceImageFormatProperties2 = + Hook_GetPhysicalDeviceImageFormatProperties2_NotSupportedOnce; + + PFN_vkGetPhysicalDeviceSurfaceFormats2KHR + pfnGetPhysicalDeviceSurfaceFormats2KHR = + reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormats2KHR>( + vkGetInstanceProcAddr(mVkInstance, + "vkGetPhysicalDeviceSurfaceFormats2KHR")); + ASSERT_NE(nullptr, pfnGetPhysicalDeviceSurfaceFormats2KHR) + << "Could not get pointer to vkGetPhysicalDeviceSurfaceFormats2KHR"; + + VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo2{}; + surfaceInfo2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; + surfaceInfo2.pNext = nullptr; + surfaceInfo2.surface = mSurface; + + uint32_t formatCount = 0; + VkResult res = pfnGetPhysicalDeviceSurfaceFormats2KHR( + mPhysicalDev, &surfaceInfo2, &formatCount, nullptr); + + // If the loader never tries a second format, it might fail or 0-out the + // formatCount. The patch ensures it continues to the next format rather + // than bailing out on the first NOT_SUPPORTED. + ASSERT_EQ(VK_SUCCESS, res) + << "vkGetPhysicalDeviceSurfaceFormats2KHR failed unexpectedly"; + ASSERT_GT(formatCount, 0U) + << "No surface formats found; the loader may have bailed early."; + + std::vector<VkSurfaceFormat2KHR> formats(formatCount); + for (auto& f : formats) { + f.sType = VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR; + f.pNext = nullptr; + } + res = pfnGetPhysicalDeviceSurfaceFormats2KHR(mPhysicalDev, &surfaceInfo2, + &formatCount, formats.data()); + ASSERT_EQ(VK_SUCCESS, res) << "Failed to retrieve surface formats"; + + LOGI( + "SurfaceFormats2KHR_IgnoreNotSupported test: found %u formats after " + "ignoring NOT_SUPPORTED", + formatCount); + + cleanUpSwapchainForTest(); +} + +} // namespace + +namespace { + +TEST_F(AImageReaderVulkanSwapchainTest, MutableFormatSwapchainTest) { + // Test swapchain with mutable format extension + std::vector<const char*> instanceLayers; + std::vector<const char*> deviceLayers; + std::vector<const char*> deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, + VK_KHR_MAINTENANCE2_EXTENSION_NAME, + VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME}; + + createVulkanInstance(instanceLayers); + createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3); + getANativeWindowFromReader(); + createVulkanSurface(); + pickPhysicalDeviceAndQueueFamily(); + createDeviceAndGetQueue(deviceLayers, deviceExtensions); + + ASSERT_NE((VkDevice)VK_NULL_HANDLE, mDevice); + ASSERT_NE((VkSurfaceKHR)VK_NULL_HANDLE, mSurface); + + VkSurfaceCapabilitiesKHR surfaceCaps{}; + VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDev, mSurface, + &surfaceCaps)); + + uint32_t formatCount = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface, &formatCount, + nullptr); + ASSERT_GT(formatCount, 0U); + std::vector<VkSurfaceFormatKHR> formats(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface, &formatCount, + formats.data()); + + VkFormat viewFormats[2] = {formats[0].format, formats[0].format}; + if (formatCount > 1) { + viewFormats[1] = formats[1].format; + } + + VkImageFormatListCreateInfoKHR formatList{}; + formatList.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR; + formatList.viewFormatCount = 2; + formatList.pViewFormats = viewFormats; + + VkSwapchainCreateInfoKHR swapchainInfo{}; + swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainInfo.pNext = &formatList; + swapchainInfo.surface = mSurface; + swapchainInfo.minImageCount = surfaceCaps.minImageCount + 1; + swapchainInfo.imageFormat = formats[0].format; + swapchainInfo.imageColorSpace = formats[0].colorSpace; + swapchainInfo.imageExtent = surfaceCaps.currentExtent; + swapchainInfo.imageArrayLayers = 1; + swapchainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchainInfo.preTransform = surfaceCaps.currentTransform; + swapchainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + swapchainInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; + swapchainInfo.clipped = VK_TRUE; + + swapchainInfo.flags = VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR; + + uint32_t queueFamilyIndices[] = {mPresentQueueFamily}; + swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainInfo.queueFamilyIndexCount = 1; + swapchainInfo.pQueueFamilyIndices = queueFamilyIndices; + + VkResult res = + vkCreateSwapchainKHR(mDevice, &swapchainInfo, nullptr, &mSwapchain); + if (res == VK_SUCCESS) { + LOGI("Mutable format swapchain created successfully"); + + uint32_t imageCount = 0; + vkGetSwapchainImagesKHR(mDevice, mSwapchain, &imageCount, nullptr); + ASSERT_GT(imageCount, 0U); + } else { + LOGI( + "Mutable format swapchain creation failed (extension may not be " + "supported)"); + } + + cleanUpSwapchainForTest(); +} + +} // namespace + +} // namespace libvulkantest + +} // namespace android diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 8c0cce26ba..531a623e90 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -56,13 +56,15 @@ inline bool IsIntegral(double value) { // Floating point fields of Vulkan structure use single precision. The string // output of max double value in c++ will be larger than Java double's infinity // value. Below fake double max/min values are only to serve the safe json text -// parsing in between C++ and Java, becasue Java json library simply cannot +// parsing in between C++ and Java, because Java json library simply cannot // handle infinity. static const double SAFE_DOUBLE_MAX = 0.99 * std::numeric_limits<double>::max(); static const double SAFE_DOUBLE_MIN = -SAFE_DOUBLE_MAX; -template <typename T> struct EnumTraits; -template <> struct EnumTraits<VkPhysicalDeviceType> { +template <typename T> +struct EnumTraits; +template <> +struct EnumTraits<VkPhysicalDeviceType> { static bool exist(uint32_t e) { switch (e) { case VK_PHYSICAL_DEVICE_TYPE_OTHER: @@ -76,7 +78,8 @@ template <> struct EnumTraits<VkPhysicalDeviceType> { } }; -template <> struct EnumTraits<VkFormat> { +template <> +struct EnumTraits<VkFormat> { static bool exist(uint32_t e) { switch (e) { case VK_FORMAT_UNDEFINED: @@ -482,694 +485,1447 @@ struct EnumTraits<VkImageLayout> { template <typename Visitor> inline bool Iterate(Visitor* visitor, VkExtent3D* extents) { + return visitor->Visit("width", &extents->width) && + visitor->Visit("height", &extents->height) && + visitor->Visit("depth", &extents->depth); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkConformanceVersionKHR* version) { + return visitor->Visit("major", &version->major) && + visitor->Visit("minor", &version->minor) && + visitor->Visit("subminor", &version->subminor) && + visitor->Visit("patch", &version->patch); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkMemoryType* type) { + return visitor->Visit("propertyFlags", &type->propertyFlags) && + visitor->Visit("heapIndex", &type->heapIndex); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkMemoryHeap* heap) { + return visitor->Visit("size", &heap->size) && + visitor->Visit("flags", &heap->flags); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonCore11* core) { + return visitor->Visit("properties", &core->properties) && + visitor->Visit("features", &core->features); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonCore12* core) { + return visitor->Visit("properties", &core->properties) && + visitor->Visit("features", &core->features); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonCore13* core) { + return visitor->Visit("properties", &core->properties) && + visitor->Visit("features", &core->features); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonCore14* core) { + return visitor->Visit("properties", &core->properties) && + visitor->Visit("features", &core->features); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonKHRVariablePointers* structs) { + return visitor->Visit("variablePointerFeaturesKHR", + &structs->variable_pointer_features_khr) && + visitor->Visit("variablePointersFeaturesKHR", + &structs->variable_pointers_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonKHRShaderFloat16Int8* structs) { + return visitor->Visit("shaderFloat16Int8FeaturesKHR", + &structs->shader_float16_int8_features_khr) && + visitor->Visit("float16Int8FeaturesKHR", + &structs->float16_int8_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonExtImage2dViewOf3d* structs) { + return visitor->Visit("image2DViewOf3DFeaturesEXT", + &structs->image_2d_view_of_3d_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonExtCustomBorderColor* structs) { + return visitor->Visit("customBorderColorFeaturesEXT", + &structs->custom_border_color_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkJsonExtPrimitiveTopologyListRestart* structs) { + return visitor->Visit("primitiveTopologyListRestartFeaturesEXT", + &structs->primitive_topology_list_restart_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonExtProvokingVertex* structs) { + return visitor->Visit("provokingVertexFeaturesEXT", + &structs->provoking_vertex_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonKHRIndexTypeUint8* structs) { + return visitor->Visit("indexTypeUint8FeaturesKHR", + &structs->index_type_uint8_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonExtIndexTypeUint8* structs) { + return visitor->Visit("indexTypeUint8FeaturesEXT", + &structs->index_type_uint8_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkJsonKHRVertexAttributeDivisor* structs) { + return visitor->Visit("vertexAttributeDivisorFeaturesKHR", + &structs->vertex_attribute_divisor_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkJsonExtVertexAttributeDivisor* structs) { + return visitor->Visit("vertexAttributeDivisorFeaturesEXT", + &structs->vertex_attribute_divisor_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonExtTransformFeedback* structs) { + return visitor->Visit("transformFeedbackFeaturesEXT", + &structs->transform_feedback_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkJsonKHRShaderSubgroupUniformControlFlow* structs) { + return visitor->Visit( + "shaderSubgroupUniformControlFlowFeaturesKHR", + &structs->shader_subgroup_uniform_control_flow_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkJsonKHRShaderSubgroupExtendedTypes* structs) { + return visitor->Visit("shaderSubgroupExtendedTypesFeaturesKHR", + &structs->shader_subgroup_extended_types_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonKHR8bitStorage* structs) { + return visitor->Visit("bit8StorageFeaturesKHR", + &structs->bit8_storage_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkJsonKHRShaderIntegerDotProduct* structs) { + return visitor->Visit("shaderIntegerDotProductFeaturesKHR", + &structs->shader_integer_dot_product_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkJsonIMGRelaxedLineRasterization* structs) { + return visitor->Visit("relaxedLineRasterizationFeaturesIMG", + &structs->relaxed_line_rasterization_features_img); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonKHRLineRasterization* structs) { + return visitor->Visit("lineRasterizationFeaturesKHR", + &structs->line_rasterization_features_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonExtLineRasterization* structs) { + return visitor->Visit("lineRasterizationFeaturesEXT", + &structs->line_rasterization_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkJsonExtPrimitivesGeneratedQuery* structs) { + return visitor->Visit("primitivesGeneratedQueryFeaturesEXT", + &structs->primitives_generated_query_features_ext); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonKHRShaderFloatControls* structs) { + return visitor->Visit("floatControlsPropertiesKHR", + &structs->float_controls_properties_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkJsonKHRDriverProperties* structs) { + return visitor->Visit("driverPropertiesKHR", &structs->driver_properties_khr); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceFloatControlsProperties* properties) { return - visitor->Visit("width", &extents->width) && - visitor->Visit("height", &extents->height) && - visitor->Visit("depth", &extents->depth); + + visitor->Visit("denormBehaviorIndependence", + &properties->denormBehaviorIndependence) && + visitor->Visit("roundingModeIndependence", + &properties->roundingModeIndependence) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat16", + &properties->shaderSignedZeroInfNanPreserveFloat16) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat32", + &properties->shaderSignedZeroInfNanPreserveFloat32) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat64", + &properties->shaderSignedZeroInfNanPreserveFloat64) && + visitor->Visit("shaderDenormPreserveFloat16", + &properties->shaderDenormPreserveFloat16) && + visitor->Visit("shaderDenormPreserveFloat32", + &properties->shaderDenormPreserveFloat32) && + visitor->Visit("shaderDenormPreserveFloat64", + &properties->shaderDenormPreserveFloat64) && + visitor->Visit("shaderDenormFlushToZeroFloat16", + &properties->shaderDenormFlushToZeroFloat16) && + visitor->Visit("shaderDenormFlushToZeroFloat32", + &properties->shaderDenormFlushToZeroFloat32) && + visitor->Visit("shaderDenormFlushToZeroFloat64", + &properties->shaderDenormFlushToZeroFloat64) && + visitor->Visit("shaderRoundingModeRTEFloat16", + &properties->shaderRoundingModeRTEFloat16) && + visitor->Visit("shaderRoundingModeRTEFloat32", + &properties->shaderRoundingModeRTEFloat32) && + visitor->Visit("shaderRoundingModeRTEFloat64", + &properties->shaderRoundingModeRTEFloat64) && + visitor->Visit("shaderRoundingModeRTZFloat16", + &properties->shaderRoundingModeRTZFloat16) && + visitor->Visit("shaderRoundingModeRTZFloat32", + &properties->shaderRoundingModeRTZFloat32) && + visitor->Visit("shaderRoundingModeRTZFloat64", + &properties->shaderRoundingModeRTZFloat64); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkImageFormatProperties* properties) { +inline bool Iterate(Visitor* visitor, VkPhysicalDeviceProperties* properties) { return - visitor->Visit("maxExtent", &properties->maxExtent) && - visitor->Visit("maxMipLevels", &properties->maxMipLevels) && - visitor->Visit("maxArrayLayers", &properties->maxArrayLayers) && - visitor->Visit("sampleCounts", &properties->sampleCounts) && - visitor->Visit("maxResourceSize", &properties->maxResourceSize); + + visitor->Visit("apiVersion", &properties->apiVersion) && + visitor->Visit("driverVersion", &properties->driverVersion) && + visitor->Visit("vendorID", &properties->vendorID) && + visitor->Visit("deviceID", &properties->deviceID) && + visitor->Visit("deviceType", &properties->deviceType) && + visitor->Visit("deviceName", &properties->deviceName) && + visitor->Visit("pipelineCacheUUID", &properties->pipelineCacheUUID) && + visitor->Visit("limits", &properties->limits) && + visitor->Visit("sparseProperties", &properties->sparseProperties); } -// clang-format off template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceLimits* limits) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceMemoryProperties* properties) { return - visitor->Visit("maxImageDimension1D", &limits->maxImageDimension1D) && - visitor->Visit("maxImageDimension2D", &limits->maxImageDimension2D) && - visitor->Visit("maxImageDimension3D", &limits->maxImageDimension3D) && - visitor->Visit("maxImageDimensionCube", &limits->maxImageDimensionCube) && - visitor->Visit("maxImageArrayLayers", &limits->maxImageArrayLayers) && - visitor->Visit("maxTexelBufferElements", &limits->maxTexelBufferElements) && - visitor->Visit("maxUniformBufferRange", &limits->maxUniformBufferRange) && - visitor->Visit("maxStorageBufferRange", &limits->maxStorageBufferRange) && - visitor->Visit("maxPushConstantsSize", &limits->maxPushConstantsSize) && - visitor->Visit("maxMemoryAllocationCount", &limits->maxMemoryAllocationCount) && - visitor->Visit("maxSamplerAllocationCount", &limits->maxSamplerAllocationCount) && - visitor->Visit("bufferImageGranularity", &limits->bufferImageGranularity) && - visitor->Visit("sparseAddressSpaceSize", &limits->sparseAddressSpaceSize) && - visitor->Visit("maxBoundDescriptorSets", &limits->maxBoundDescriptorSets) && - visitor->Visit("maxPerStageDescriptorSamplers", &limits->maxPerStageDescriptorSamplers) && - visitor->Visit("maxPerStageDescriptorUniformBuffers", &limits->maxPerStageDescriptorUniformBuffers) && - visitor->Visit("maxPerStageDescriptorStorageBuffers", &limits->maxPerStageDescriptorStorageBuffers) && - visitor->Visit("maxPerStageDescriptorSampledImages", &limits->maxPerStageDescriptorSampledImages) && - visitor->Visit("maxPerStageDescriptorStorageImages", &limits->maxPerStageDescriptorStorageImages) && - visitor->Visit("maxPerStageDescriptorInputAttachments", &limits->maxPerStageDescriptorInputAttachments) && - visitor->Visit("maxPerStageResources", &limits->maxPerStageResources) && - visitor->Visit("maxDescriptorSetSamplers", &limits->maxDescriptorSetSamplers) && - visitor->Visit("maxDescriptorSetUniformBuffers", &limits->maxDescriptorSetUniformBuffers) && - visitor->Visit("maxDescriptorSetUniformBuffersDynamic", &limits->maxDescriptorSetUniformBuffersDynamic) && - visitor->Visit("maxDescriptorSetStorageBuffers", &limits->maxDescriptorSetStorageBuffers) && - visitor->Visit("maxDescriptorSetStorageBuffersDynamic", &limits->maxDescriptorSetStorageBuffersDynamic) && - visitor->Visit("maxDescriptorSetSampledImages", &limits->maxDescriptorSetSampledImages) && - visitor->Visit("maxDescriptorSetStorageImages", &limits->maxDescriptorSetStorageImages) && - visitor->Visit("maxDescriptorSetInputAttachments", &limits->maxDescriptorSetInputAttachments) && - visitor->Visit("maxVertexInputAttributes", &limits->maxVertexInputAttributes) && - visitor->Visit("maxVertexInputBindings", &limits->maxVertexInputBindings) && - visitor->Visit("maxVertexInputAttributeOffset", &limits->maxVertexInputAttributeOffset) && - visitor->Visit("maxVertexInputBindingStride", &limits->maxVertexInputBindingStride) && - visitor->Visit("maxVertexOutputComponents", &limits->maxVertexOutputComponents) && - visitor->Visit("maxTessellationGenerationLevel", &limits->maxTessellationGenerationLevel) && - visitor->Visit("maxTessellationPatchSize", &limits->maxTessellationPatchSize) && - visitor->Visit("maxTessellationControlPerVertexInputComponents", &limits->maxTessellationControlPerVertexInputComponents) && - visitor->Visit("maxTessellationControlPerVertexOutputComponents", &limits->maxTessellationControlPerVertexOutputComponents) && - visitor->Visit("maxTessellationControlPerPatchOutputComponents", &limits->maxTessellationControlPerPatchOutputComponents) && - visitor->Visit("maxTessellationControlTotalOutputComponents", &limits->maxTessellationControlTotalOutputComponents) && - visitor->Visit("maxTessellationEvaluationInputComponents", &limits->maxTessellationEvaluationInputComponents) && - visitor->Visit("maxTessellationEvaluationOutputComponents", &limits->maxTessellationEvaluationOutputComponents) && - visitor->Visit("maxGeometryShaderInvocations", &limits->maxGeometryShaderInvocations) && - visitor->Visit("maxGeometryInputComponents", &limits->maxGeometryInputComponents) && - visitor->Visit("maxGeometryOutputComponents", &limits->maxGeometryOutputComponents) && - visitor->Visit("maxGeometryOutputVertices", &limits->maxGeometryOutputVertices) && - visitor->Visit("maxGeometryTotalOutputComponents", &limits->maxGeometryTotalOutputComponents) && - visitor->Visit("maxFragmentInputComponents", &limits->maxFragmentInputComponents) && - visitor->Visit("maxFragmentOutputAttachments", &limits->maxFragmentOutputAttachments) && - visitor->Visit("maxFragmentDualSrcAttachments", &limits->maxFragmentDualSrcAttachments) && - visitor->Visit("maxFragmentCombinedOutputResources", &limits->maxFragmentCombinedOutputResources) && - visitor->Visit("maxComputeSharedMemorySize", &limits->maxComputeSharedMemorySize) && - visitor->Visit("maxComputeWorkGroupCount", &limits->maxComputeWorkGroupCount) && - visitor->Visit("maxComputeWorkGroupInvocations", &limits->maxComputeWorkGroupInvocations) && - visitor->Visit("maxComputeWorkGroupSize", &limits->maxComputeWorkGroupSize) && - visitor->Visit("subPixelPrecisionBits", &limits->subPixelPrecisionBits) && - visitor->Visit("subTexelPrecisionBits", &limits->subTexelPrecisionBits) && - visitor->Visit("mipmapPrecisionBits", &limits->mipmapPrecisionBits) && - visitor->Visit("maxDrawIndexedIndexValue", &limits->maxDrawIndexedIndexValue) && - visitor->Visit("maxDrawIndirectCount", &limits->maxDrawIndirectCount) && - visitor->Visit("maxSamplerLodBias", &limits->maxSamplerLodBias) && - visitor->Visit("maxSamplerAnisotropy", &limits->maxSamplerAnisotropy) && - visitor->Visit("maxViewports", &limits->maxViewports) && - visitor->Visit("maxViewportDimensions", &limits->maxViewportDimensions) && - visitor->Visit("viewportBoundsRange", &limits->viewportBoundsRange) && - visitor->Visit("viewportSubPixelBits", &limits->viewportSubPixelBits) && - visitor->Visit("minMemoryMapAlignment", &limits->minMemoryMapAlignment) && - visitor->Visit("minTexelBufferOffsetAlignment", &limits->minTexelBufferOffsetAlignment) && - visitor->Visit("minUniformBufferOffsetAlignment", &limits->minUniformBufferOffsetAlignment) && - visitor->Visit("minStorageBufferOffsetAlignment", &limits->minStorageBufferOffsetAlignment) && - visitor->Visit("minTexelOffset", &limits->minTexelOffset) && - visitor->Visit("maxTexelOffset", &limits->maxTexelOffset) && - visitor->Visit("minTexelGatherOffset", &limits->minTexelGatherOffset) && - visitor->Visit("maxTexelGatherOffset", &limits->maxTexelGatherOffset) && - visitor->Visit("minInterpolationOffset", &limits->minInterpolationOffset) && - visitor->Visit("maxInterpolationOffset", &limits->maxInterpolationOffset) && - visitor->Visit("subPixelInterpolationOffsetBits", &limits->subPixelInterpolationOffsetBits) && - visitor->Visit("maxFramebufferWidth", &limits->maxFramebufferWidth) && - visitor->Visit("maxFramebufferHeight", &limits->maxFramebufferHeight) && - visitor->Visit("maxFramebufferLayers", &limits->maxFramebufferLayers) && - visitor->Visit("framebufferColorSampleCounts", &limits->framebufferColorSampleCounts) && - visitor->Visit("framebufferDepthSampleCounts", &limits->framebufferDepthSampleCounts) && - visitor->Visit("framebufferStencilSampleCounts", &limits->framebufferStencilSampleCounts) && - visitor->Visit("framebufferNoAttachmentsSampleCounts", &limits->framebufferNoAttachmentsSampleCounts) && - visitor->Visit("maxColorAttachments", &limits->maxColorAttachments) && - visitor->Visit("sampledImageColorSampleCounts", &limits->sampledImageColorSampleCounts) && - visitor->Visit("sampledImageIntegerSampleCounts", &limits->sampledImageIntegerSampleCounts) && - visitor->Visit("sampledImageDepthSampleCounts", &limits->sampledImageDepthSampleCounts) && - visitor->Visit("sampledImageStencilSampleCounts", &limits->sampledImageStencilSampleCounts) && - visitor->Visit("storageImageSampleCounts", &limits->storageImageSampleCounts) && - visitor->Visit("maxSampleMaskWords", &limits->maxSampleMaskWords) && - visitor->Visit("timestampComputeAndGraphics", &limits->timestampComputeAndGraphics) && - visitor->Visit("timestampPeriod", &limits->timestampPeriod) && - visitor->Visit("maxClipDistances", &limits->maxClipDistances) && - visitor->Visit("maxCullDistances", &limits->maxCullDistances) && - visitor->Visit("maxCombinedClipAndCullDistances", &limits->maxCombinedClipAndCullDistances) && - visitor->Visit("discreteQueuePriorities", &limits->discreteQueuePriorities) && - visitor->Visit("pointSizeRange", &limits->pointSizeRange) && - visitor->Visit("lineWidthRange", &limits->lineWidthRange) && - visitor->Visit("pointSizeGranularity", &limits->pointSizeGranularity) && - visitor->Visit("lineWidthGranularity", &limits->lineWidthGranularity) && - visitor->Visit("strictLines", &limits->strictLines) && - visitor->Visit("standardSampleLocations", &limits->standardSampleLocations) && - visitor->Visit("optimalBufferCopyOffsetAlignment", &limits->optimalBufferCopyOffsetAlignment) && - visitor->Visit("optimalBufferCopyRowPitchAlignment", &limits->optimalBufferCopyRowPitchAlignment) && - visitor->Visit("nonCoherentAtomSize", &limits->nonCoherentAtomSize); + + visitor->Visit("memoryTypeCount", &properties->memoryTypeCount) && + visitor->VisitArray("memoryTypes", properties->memoryTypeCount, + &properties->memoryTypes) && + visitor->Visit("memoryHeapCount", &properties->memoryHeapCount) && + visitor->VisitArray("memoryHeaps", properties->memoryHeapCount, + &properties->memoryHeaps); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceSparseProperties* properties) { + VkPhysicalDeviceSubgroupProperties* properties) { return - visitor->Visit("residencyStandard2DBlockShape", &properties->residencyStandard2DBlockShape) && - visitor->Visit("residencyStandard2DMultisampleBlockShape", &properties->residencyStandard2DMultisampleBlockShape) && - visitor->Visit("residencyStandard3DBlockShape", &properties->residencyStandard3DBlockShape) && - visitor->Visit("residencyAlignedMipSize", &properties->residencyAlignedMipSize) && - visitor->Visit("residencyNonResidentStrict", &properties->residencyNonResidentStrict); + + visitor->Visit("subgroupSize", &properties->subgroupSize) && + visitor->Visit("supportedStages", &properties->supportedStages) && + visitor->Visit("supportedOperations", &properties->supportedOperations) && + visitor->Visit("quadOperationsInAllStages", + &properties->quadOperationsInAllStages); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceProperties* properties) { + VkPhysicalDevicePointClippingProperties* properties) { return - visitor->Visit("apiVersion", &properties->apiVersion) && - visitor->Visit("driverVersion", &properties->driverVersion) && - visitor->Visit("vendorID", &properties->vendorID) && - visitor->Visit("deviceID", &properties->deviceID) && - visitor->Visit("deviceType", &properties->deviceType) && - visitor->Visit("deviceName", &properties->deviceName) && - visitor->Visit("pipelineCacheUUID", &properties->pipelineCacheUUID) && - visitor->Visit("limits", &properties->limits) && - visitor->Visit("sparseProperties", &properties->sparseProperties); + + visitor->Visit("pointClippingBehavior", + &properties->pointClippingBehavior); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceFeatures* features) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceMultiviewProperties* properties) { return - visitor->Visit("robustBufferAccess", &features->robustBufferAccess) && - visitor->Visit("fullDrawIndexUint32", &features->fullDrawIndexUint32) && - visitor->Visit("imageCubeArray", &features->imageCubeArray) && - visitor->Visit("independentBlend", &features->independentBlend) && - visitor->Visit("geometryShader", &features->geometryShader) && - visitor->Visit("tessellationShader", &features->tessellationShader) && - visitor->Visit("sampleRateShading", &features->sampleRateShading) && - visitor->Visit("dualSrcBlend", &features->dualSrcBlend) && - visitor->Visit("logicOp", &features->logicOp) && - visitor->Visit("multiDrawIndirect", &features->multiDrawIndirect) && - visitor->Visit("drawIndirectFirstInstance", &features->drawIndirectFirstInstance) && - visitor->Visit("depthClamp", &features->depthClamp) && - visitor->Visit("depthBiasClamp", &features->depthBiasClamp) && - visitor->Visit("fillModeNonSolid", &features->fillModeNonSolid) && - visitor->Visit("depthBounds", &features->depthBounds) && - visitor->Visit("wideLines", &features->wideLines) && - visitor->Visit("largePoints", &features->largePoints) && - visitor->Visit("alphaToOne", &features->alphaToOne) && - visitor->Visit("multiViewport", &features->multiViewport) && - visitor->Visit("samplerAnisotropy", &features->samplerAnisotropy) && - visitor->Visit("textureCompressionETC2", &features->textureCompressionETC2) && - visitor->Visit("textureCompressionASTC_LDR", &features->textureCompressionASTC_LDR) && - visitor->Visit("textureCompressionBC", &features->textureCompressionBC) && - visitor->Visit("occlusionQueryPrecise", &features->occlusionQueryPrecise) && - visitor->Visit("pipelineStatisticsQuery", &features->pipelineStatisticsQuery) && - visitor->Visit("vertexPipelineStoresAndAtomics", &features->vertexPipelineStoresAndAtomics) && - visitor->Visit("fragmentStoresAndAtomics", &features->fragmentStoresAndAtomics) && - visitor->Visit("shaderTessellationAndGeometryPointSize", &features->shaderTessellationAndGeometryPointSize) && - visitor->Visit("shaderImageGatherExtended", &features->shaderImageGatherExtended) && - visitor->Visit("shaderStorageImageExtendedFormats", &features->shaderStorageImageExtendedFormats) && - visitor->Visit("shaderStorageImageMultisample", &features->shaderStorageImageMultisample) && - visitor->Visit("shaderStorageImageReadWithoutFormat", &features->shaderStorageImageReadWithoutFormat) && - visitor->Visit("shaderStorageImageWriteWithoutFormat", &features->shaderStorageImageWriteWithoutFormat) && - visitor->Visit("shaderUniformBufferArrayDynamicIndexing", &features->shaderUniformBufferArrayDynamicIndexing) && - visitor->Visit("shaderSampledImageArrayDynamicIndexing", &features->shaderSampledImageArrayDynamicIndexing) && - visitor->Visit("shaderStorageBufferArrayDynamicIndexing", &features->shaderStorageBufferArrayDynamicIndexing) && - visitor->Visit("shaderStorageImageArrayDynamicIndexing", &features->shaderStorageImageArrayDynamicIndexing) && - visitor->Visit("shaderClipDistance", &features->shaderClipDistance) && - visitor->Visit("shaderCullDistance", &features->shaderCullDistance) && - visitor->Visit("shaderFloat64", &features->shaderFloat64) && - visitor->Visit("shaderInt64", &features->shaderInt64) && - visitor->Visit("shaderInt16", &features->shaderInt16) && - visitor->Visit("shaderResourceResidency", &features->shaderResourceResidency) && - visitor->Visit("shaderResourceMinLod", &features->shaderResourceMinLod) && - visitor->Visit("sparseBinding", &features->sparseBinding) && - visitor->Visit("sparseResidencyBuffer", &features->sparseResidencyBuffer) && - visitor->Visit("sparseResidencyImage2D", &features->sparseResidencyImage2D) && - visitor->Visit("sparseResidencyImage3D", &features->sparseResidencyImage3D) && - visitor->Visit("sparseResidency2Samples", &features->sparseResidency2Samples) && - visitor->Visit("sparseResidency4Samples", &features->sparseResidency4Samples) && - visitor->Visit("sparseResidency8Samples", &features->sparseResidency8Samples) && - visitor->Visit("sparseResidency16Samples", &features->sparseResidency16Samples) && - visitor->Visit("sparseResidencyAliased", &features->sparseResidencyAliased) && - visitor->Visit("variableMultisampleRate", &features->variableMultisampleRate) && - visitor->Visit("inheritedQueries", &features->inheritedQueries); + + visitor->Visit("maxMultiviewViewCount", + &properties->maxMultiviewViewCount) && + visitor->Visit("maxMultiviewInstanceIndex", + &properties->maxMultiviewInstanceIndex); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkJsonCore12* core) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceIDProperties* properties) { return - visitor->Visit("features", &core->features) && - visitor->Visit("properties", &core->properties); + + visitor->Visit("deviceUUID", &properties->deviceUUID) && + visitor->Visit("driverUUID", &properties->driverUUID) && + visitor->Visit("deviceLUID", &properties->deviceLUID) && + visitor->Visit("deviceNodeMask", &properties->deviceNodeMask) && + visitor->Visit("deviceLUIDValid", &properties->deviceLUIDValid); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Properties* properties) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceMaintenance3Properties* properties) { return - visitor->Visit("driverID", &properties->driverID) && - visitor->Visit("driverName", &properties->driverName) && - visitor->Visit("driverInfo", &properties->driverInfo) && - visitor->Visit("conformanceVersion", &properties->conformanceVersion) && - visitor->Visit("denormBehaviorIndependence", &properties->denormBehaviorIndependence) && - visitor->Visit("roundingModeIndependence", &properties->roundingModeIndependence) && - visitor->Visit("shaderSignedZeroInfNanPreserveFloat16", &properties->shaderSignedZeroInfNanPreserveFloat16) && - visitor->Visit("shaderSignedZeroInfNanPreserveFloat32", &properties->shaderSignedZeroInfNanPreserveFloat32) && - visitor->Visit("shaderSignedZeroInfNanPreserveFloat64", &properties->shaderSignedZeroInfNanPreserveFloat64) && - visitor->Visit("shaderDenormPreserveFloat16", &properties->shaderDenormPreserveFloat16) && - visitor->Visit("shaderDenormPreserveFloat32", &properties->shaderDenormPreserveFloat32) && - visitor->Visit("shaderDenormPreserveFloat64", &properties->shaderDenormPreserveFloat64) && - visitor->Visit("shaderDenormFlushToZeroFloat16", &properties->shaderDenormFlushToZeroFloat16) && - visitor->Visit("shaderDenormFlushToZeroFloat32", &properties->shaderDenormFlushToZeroFloat32) && - visitor->Visit("shaderDenormFlushToZeroFloat64", &properties->shaderDenormFlushToZeroFloat64) && - visitor->Visit("shaderRoundingModeRTEFloat16", &properties->shaderRoundingModeRTEFloat16) && - visitor->Visit("shaderRoundingModeRTEFloat32", &properties->shaderRoundingModeRTEFloat32) && - visitor->Visit("shaderRoundingModeRTEFloat64", &properties->shaderRoundingModeRTEFloat64) && - visitor->Visit("shaderRoundingModeRTZFloat16", &properties->shaderRoundingModeRTZFloat16) && - visitor->Visit("shaderRoundingModeRTZFloat32", &properties->shaderRoundingModeRTZFloat32) && - visitor->Visit("shaderRoundingModeRTZFloat64", &properties->shaderRoundingModeRTZFloat64) && - visitor->Visit("maxUpdateAfterBindDescriptorsInAllPools", &properties->maxUpdateAfterBindDescriptorsInAllPools) && - visitor->Visit("shaderUniformBufferArrayNonUniformIndexingNative", &properties->shaderUniformBufferArrayNonUniformIndexingNative) && - visitor->Visit("shaderSampledImageArrayNonUniformIndexingNative", &properties->shaderSampledImageArrayNonUniformIndexingNative) && - visitor->Visit("shaderStorageBufferArrayNonUniformIndexingNative", &properties->shaderStorageBufferArrayNonUniformIndexingNative) && - visitor->Visit("shaderStorageImageArrayNonUniformIndexingNative", &properties->shaderStorageImageArrayNonUniformIndexingNative) && - visitor->Visit("shaderInputAttachmentArrayNonUniformIndexingNative", &properties->shaderInputAttachmentArrayNonUniformIndexingNative) && - visitor->Visit("robustBufferAccessUpdateAfterBind", &properties->robustBufferAccessUpdateAfterBind) && - visitor->Visit("quadDivergentImplicitLod", &properties->quadDivergentImplicitLod) && - visitor->Visit("maxPerStageDescriptorUpdateAfterBindSamplers", &properties->maxPerStageDescriptorUpdateAfterBindSamplers) && - visitor->Visit("maxPerStageDescriptorUpdateAfterBindUniformBuffers", &properties->maxPerStageDescriptorUpdateAfterBindUniformBuffers) && - visitor->Visit("maxPerStageDescriptorUpdateAfterBindStorageBuffers", &properties->maxPerStageDescriptorUpdateAfterBindStorageBuffers) && - visitor->Visit("maxPerStageDescriptorUpdateAfterBindSampledImages", &properties->maxPerStageDescriptorUpdateAfterBindSampledImages) && - visitor->Visit("maxPerStageDescriptorUpdateAfterBindStorageImages", &properties->maxPerStageDescriptorUpdateAfterBindStorageImages) && - visitor->Visit("maxPerStageDescriptorUpdateAfterBindInputAttachments", &properties->maxPerStageDescriptorUpdateAfterBindInputAttachments) && - visitor->Visit("maxPerStageUpdateAfterBindResources", &properties->maxPerStageUpdateAfterBindResources) && - visitor->Visit("maxDescriptorSetUpdateAfterBindSamplers", &properties->maxDescriptorSetUpdateAfterBindSamplers) && - visitor->Visit("maxDescriptorSetUpdateAfterBindUniformBuffers", &properties->maxDescriptorSetUpdateAfterBindUniformBuffers) && - visitor->Visit("maxDescriptorSetUpdateAfterBindUniformBuffersDynamic", &properties->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic) && - visitor->Visit("maxDescriptorSetUpdateAfterBindStorageBuffers", &properties->maxDescriptorSetUpdateAfterBindStorageBuffers) && - visitor->Visit("maxDescriptorSetUpdateAfterBindStorageBuffersDynamic", &properties->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic) && - visitor->Visit("maxDescriptorSetUpdateAfterBindSampledImages", &properties->maxDescriptorSetUpdateAfterBindSampledImages) && - visitor->Visit("maxDescriptorSetUpdateAfterBindStorageImages", &properties->maxDescriptorSetUpdateAfterBindStorageImages) && - visitor->Visit("maxDescriptorSetUpdateAfterBindInputAttachments", &properties->maxDescriptorSetUpdateAfterBindInputAttachments) && - visitor->Visit("supportedDepthResolveModes", &properties->supportedDepthResolveModes) && - visitor->Visit("supportedStencilResolveModes", &properties->supportedStencilResolveModes) && - visitor->Visit("independentResolveNone", &properties->independentResolveNone) && - visitor->Visit("independentResolve", &properties->independentResolve) && - visitor->Visit("filterMinmaxSingleComponentFormats", &properties->filterMinmaxSingleComponentFormats) && - visitor->Visit("filterMinmaxImageComponentMapping", &properties->filterMinmaxImageComponentMapping) && - visitor->Visit("maxTimelineSemaphoreValueDifference", &properties->maxTimelineSemaphoreValueDifference) && - visitor->Visit("framebufferIntegerColorSampleCounts", &properties->framebufferIntegerColorSampleCounts); -} - -template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Features* features) { + + visitor->Visit("maxPerSetDescriptors", + &properties->maxPerSetDescriptors) && + visitor->Visit("maxMemoryAllocationSize", + &properties->maxMemoryAllocationSize); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceSparseProperties* properties) { return - visitor->Visit("samplerMirrorClampToEdge", &features->samplerMirrorClampToEdge) && - visitor->Visit("drawIndirectCount", &features->drawIndirectCount) && - visitor->Visit("storageBuffer8BitAccess", &features->storageBuffer8BitAccess) && - visitor->Visit("uniformAndStorageBuffer8BitAccess", &features->uniformAndStorageBuffer8BitAccess) && - visitor->Visit("storagePushConstant8", &features->storagePushConstant8) && - visitor->Visit("shaderBufferInt64Atomics", &features->shaderBufferInt64Atomics) && - visitor->Visit("shaderSharedInt64Atomics", &features->shaderSharedInt64Atomics) && - visitor->Visit("shaderFloat16", &features->shaderFloat16) && - visitor->Visit("shaderInt8", &features->shaderInt8) && - visitor->Visit("descriptorIndexing", &features->descriptorIndexing) && - visitor->Visit("shaderInputAttachmentArrayDynamicIndexing", &features->shaderInputAttachmentArrayDynamicIndexing) && - visitor->Visit("shaderUniformTexelBufferArrayDynamicIndexing", &features->shaderUniformTexelBufferArrayDynamicIndexing) && - visitor->Visit("shaderStorageTexelBufferArrayDynamicIndexing", &features->shaderStorageTexelBufferArrayDynamicIndexing) && - visitor->Visit("shaderUniformBufferArrayNonUniformIndexing", &features->shaderUniformBufferArrayNonUniformIndexing) && - visitor->Visit("shaderSampledImageArrayNonUniformIndexing", &features->shaderSampledImageArrayNonUniformIndexing) && - visitor->Visit("shaderStorageBufferArrayNonUniformIndexing", &features->shaderStorageBufferArrayNonUniformIndexing) && - visitor->Visit("shaderStorageImageArrayNonUniformIndexing", &features->shaderStorageImageArrayNonUniformIndexing) && - visitor->Visit("shaderInputAttachmentArrayNonUniformIndexing", &features->shaderInputAttachmentArrayNonUniformIndexing) && - visitor->Visit("shaderUniformTexelBufferArrayNonUniformIndexing", &features->shaderUniformTexelBufferArrayNonUniformIndexing) && - visitor->Visit("shaderStorageTexelBufferArrayNonUniformIndexing", &features->shaderStorageTexelBufferArrayNonUniformIndexing) && - visitor->Visit("descriptorBindingUniformBufferUpdateAfterBind", &features->descriptorBindingUniformBufferUpdateAfterBind) && - visitor->Visit("descriptorBindingSampledImageUpdateAfterBind", &features->descriptorBindingSampledImageUpdateAfterBind) && - visitor->Visit("descriptorBindingStorageImageUpdateAfterBind", &features->descriptorBindingStorageImageUpdateAfterBind) && - visitor->Visit("descriptorBindingStorageBufferUpdateAfterBind", &features->descriptorBindingStorageBufferUpdateAfterBind) && - visitor->Visit("descriptorBindingUniformTexelBufferUpdateAfterBind", &features->descriptorBindingUniformTexelBufferUpdateAfterBind) && - visitor->Visit("descriptorBindingStorageTexelBufferUpdateAfterBind", &features->descriptorBindingStorageTexelBufferUpdateAfterBind) && - visitor->Visit("descriptorBindingUpdateUnusedWhilePending", &features->descriptorBindingUpdateUnusedWhilePending) && - visitor->Visit("descriptorBindingPartiallyBound", &features->descriptorBindingPartiallyBound) && - visitor->Visit("descriptorBindingVariableDescriptorCount", &features->descriptorBindingVariableDescriptorCount) && - visitor->Visit("runtimeDescriptorArray", &features->runtimeDescriptorArray) && - visitor->Visit("samplerFilterMinmax", &features->samplerFilterMinmax) && - visitor->Visit("scalarBlockLayout", &features->scalarBlockLayout) && - visitor->Visit("imagelessFramebuffer", &features->imagelessFramebuffer) && - visitor->Visit("uniformBufferStandardLayout", &features->uniformBufferStandardLayout) && - visitor->Visit("shaderSubgroupExtendedTypes", &features->shaderSubgroupExtendedTypes) && - visitor->Visit("separateDepthStencilLayouts", &features->separateDepthStencilLayouts) && - visitor->Visit("hostQueryReset", &features->hostQueryReset) && - visitor->Visit("timelineSemaphore", &features->timelineSemaphore) && - visitor->Visit("bufferDeviceAddress", &features->bufferDeviceAddress) && - visitor->Visit("bufferDeviceAddressCaptureReplay", &features->bufferDeviceAddressCaptureReplay) && - visitor->Visit("bufferDeviceAddressMultiDevice", &features->bufferDeviceAddressMultiDevice) && - visitor->Visit("vulkanMemoryModel", &features->vulkanMemoryModel) && - visitor->Visit("vulkanMemoryModelDeviceScope", &features->vulkanMemoryModelDeviceScope) && - visitor->Visit("vulkanMemoryModelAvailabilityVisibilityChains", &features->vulkanMemoryModelAvailabilityVisibilityChains) && - visitor->Visit("shaderOutputViewportIndex", &features->shaderOutputViewportIndex) && - visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) && - visitor->Visit("subgroupBroadcastDynamicId", &features->subgroupBroadcastDynamicId); + + visitor->Visit("residencyStandard2DBlockShape", + &properties->residencyStandard2DBlockShape) && + visitor->Visit("residencyStandard2DMultisampleBlockShape", + &properties->residencyStandard2DMultisampleBlockShape) && + visitor->Visit("residencyStandard3DBlockShape", + &properties->residencyStandard3DBlockShape) && + visitor->Visit("residencyAlignedMipSize", + &properties->residencyAlignedMipSize) && + visitor->Visit("residencyNonResidentStrict", + &properties->residencyNonResidentStrict); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkJsonCore13* core) { +inline bool Iterate(Visitor* visitor, VkImageFormatProperties* properties) { return - visitor->Visit("features", &core->features) && - visitor->Visit("properties", &core->properties); + + visitor->Visit("maxExtent", &properties->maxExtent) && + visitor->Visit("maxMipLevels", &properties->maxMipLevels) && + visitor->Visit("maxArrayLayers", &properties->maxArrayLayers) && + visitor->Visit("sampleCounts", &properties->sampleCounts) && + visitor->Visit("maxResourceSize", &properties->maxResourceSize); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan13Properties* properties) { +inline bool Iterate(Visitor* visitor, VkQueueFamilyProperties* properties) { return - visitor->Visit("minSubgroupSize", &properties->minSubgroupSize) && - visitor->Visit("maxSubgroupSize", &properties->maxSubgroupSize) && - visitor->Visit("maxComputeWorkgroupSubgroups", &properties->maxComputeWorkgroupSubgroups) && - visitor->Visit("requiredSubgroupSizeStages", &properties->requiredSubgroupSizeStages) && - visitor->Visit("maxInlineUniformBlockSize", &properties->maxInlineUniformBlockSize) && - visitor->Visit("maxPerStageDescriptorInlineUniformBlocks", &properties->maxPerStageDescriptorInlineUniformBlocks) && - visitor->Visit("maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks", &properties->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks) && - visitor->Visit("maxDescriptorSetInlineUniformBlocks", &properties->maxDescriptorSetInlineUniformBlocks) && - visitor->Visit("maxDescriptorSetUpdateAfterBindInlineUniformBlocks", &properties->maxDescriptorSetUpdateAfterBindInlineUniformBlocks) && - visitor->Visit("maxInlineUniformTotalSize", &properties->maxInlineUniformTotalSize) && - visitor->Visit("integerDotProduct8BitUnsignedAccelerated", &properties->integerDotProduct8BitUnsignedAccelerated) && - visitor->Visit("integerDotProduct8BitSignedAccelerated", &properties->integerDotProduct8BitSignedAccelerated) && - visitor->Visit("integerDotProduct8BitMixedSignednessAccelerated", &properties->integerDotProduct8BitMixedSignednessAccelerated) && - visitor->Visit("integerDotProduct4x8BitPackedUnsignedAccelerated", &properties->integerDotProduct4x8BitPackedUnsignedAccelerated) && - visitor->Visit("integerDotProduct4x8BitPackedSignedAccelerated", &properties->integerDotProduct4x8BitPackedSignedAccelerated) && - visitor->Visit("integerDotProduct4x8BitPackedMixedSignednessAccelerated", &properties->integerDotProduct4x8BitPackedMixedSignednessAccelerated) && - visitor->Visit("integerDotProduct16BitUnsignedAccelerated", &properties->integerDotProduct16BitUnsignedAccelerated) && - visitor->Visit("integerDotProduct16BitSignedAccelerated", &properties->integerDotProduct16BitSignedAccelerated) && - visitor->Visit("integerDotProduct16BitMixedSignednessAccelerated", &properties->integerDotProduct16BitMixedSignednessAccelerated) && - visitor->Visit("integerDotProduct32BitUnsignedAccelerated", &properties->integerDotProduct32BitUnsignedAccelerated) && - visitor->Visit("integerDotProduct32BitSignedAccelerated", &properties->integerDotProduct32BitSignedAccelerated) && - visitor->Visit("integerDotProduct32BitMixedSignednessAccelerated", &properties->integerDotProduct32BitMixedSignednessAccelerated) && - visitor->Visit("integerDotProduct64BitUnsignedAccelerated", &properties->integerDotProduct64BitUnsignedAccelerated) && - visitor->Visit("integerDotProduct64BitSignedAccelerated", &properties->integerDotProduct64BitSignedAccelerated) && - visitor->Visit("integerDotProduct64BitMixedSignednessAccelerated", &properties->integerDotProduct64BitMixedSignednessAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating8BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating8BitUnsignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating8BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating8BitSignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating16BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating16BitUnsignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating16BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating16BitSignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating32BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating32BitUnsignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating32BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating32BitSignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating64BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating64BitUnsignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating64BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating64BitSignedAccelerated) && - visitor->Visit("integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated) && - visitor->Visit("storageTexelBufferOffsetAlignmentBytes", &properties->storageTexelBufferOffsetAlignmentBytes) && - visitor->Visit("storageTexelBufferOffsetSingleTexelAlignment", &properties->storageTexelBufferOffsetSingleTexelAlignment) && - visitor->Visit("uniformTexelBufferOffsetAlignmentBytes", &properties->uniformTexelBufferOffsetAlignmentBytes) && - visitor->Visit("uniformTexelBufferOffsetSingleTexelAlignment", &properties->uniformTexelBufferOffsetSingleTexelAlignment) && - visitor->Visit("maxBufferSize", &properties->maxBufferSize); -} - -template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan13Features* features) { + + visitor->Visit("queueFlags", &properties->queueFlags) && + visitor->Visit("queueCount", &properties->queueCount) && + visitor->Visit("timestampValidBits", &properties->timestampValidBits) && + visitor->Visit("minImageTransferGranularity", + &properties->minImageTransferGranularity); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkExtensionProperties* properties) { return - visitor->Visit("robustImageAccess", &features->robustImageAccess) && - visitor->Visit("inlineUniformBlock", &features->inlineUniformBlock) && - visitor->Visit("descriptorBindingInlineUniformBlockUpdateAfterBind", &features->descriptorBindingInlineUniformBlockUpdateAfterBind) && - visitor->Visit("pipelineCreationCacheControl", &features->pipelineCreationCacheControl) && - visitor->Visit("privateData", &features->privateData) && - visitor->Visit("shaderDemoteToHelperInvocation", &features->shaderDemoteToHelperInvocation) && - visitor->Visit("shaderTerminateInvocation", &features->shaderTerminateInvocation) && - visitor->Visit("subgroupSizeControl", &features->subgroupSizeControl) && - visitor->Visit("computeFullSubgroups", &features->computeFullSubgroups) && - visitor->Visit("synchronization2", &features->synchronization2) && - visitor->Visit("textureCompressionASTC_HDR", &features->textureCompressionASTC_HDR) && - visitor->Visit("shaderZeroInitializeWorkgroupMemory", &features->shaderZeroInitializeWorkgroupMemory) && - visitor->Visit("dynamicRendering", &features->dynamicRendering) && - visitor->Visit("shaderIntegerDotProduct", &features->shaderIntegerDotProduct) && - visitor->Visit("maintenance4", &features->maintenance4); + + visitor->Visit("extensionName", &properties->extensionName) && + visitor->Visit("specVersion", &properties->specVersion); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkJsonCore14* core) { +inline bool Iterate(Visitor* visitor, VkLayerProperties* properties) { return - visitor->Visit("features", &core->features) && - visitor->Visit("properties", &core->properties); + + visitor->Visit("layerName", &properties->layerName) && + visitor->Visit("specVersion", &properties->specVersion) && + visitor->Visit("implementationVersion", + &properties->implementationVersion) && + visitor->Visit("description", &properties->description); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan14Properties* properties) { +inline bool Iterate(Visitor* visitor, VkFormatProperties* properties) { return - visitor->Visit("lineSubPixelPrecisionBits", &properties->lineSubPixelPrecisionBits) && - visitor->Visit("maxVertexAttribDivisor", &properties->maxVertexAttribDivisor) && - visitor->Visit("supportsNonZeroFirstInstance", &properties->supportsNonZeroFirstInstance) && - visitor->Visit("maxPushDescriptors", &properties->maxPushDescriptors) && - visitor->Visit("dynamicRenderingLocalReadDepthStencilAttachments", &properties->dynamicRenderingLocalReadDepthStencilAttachments) && - visitor->Visit("dynamicRenderingLocalReadMultisampledAttachments", &properties->dynamicRenderingLocalReadMultisampledAttachments) && - visitor->Visit("earlyFragmentMultisampleCoverageAfterSampleCounting", &properties->earlyFragmentMultisampleCoverageAfterSampleCounting) && - visitor->Visit("earlyFragmentSampleMaskTestBeforeSampleCounting", &properties->earlyFragmentSampleMaskTestBeforeSampleCounting) && - visitor->Visit("depthStencilSwizzleOneSupport", &properties->depthStencilSwizzleOneSupport) && - visitor->Visit("polygonModePointSize", &properties->polygonModePointSize) && - visitor->Visit("nonStrictSinglePixelWideLinesUseParallelogram", &properties->nonStrictSinglePixelWideLinesUseParallelogram) && - visitor->Visit("nonStrictWideLinesUseParallelogram", &properties->nonStrictWideLinesUseParallelogram) && - visitor->Visit("blockTexelViewCompatibleMultipleLayers", &properties->blockTexelViewCompatibleMultipleLayers) && - visitor->Visit("maxCombinedImageSamplerDescriptorCount", &properties->maxCombinedImageSamplerDescriptorCount) && - visitor->Visit("fragmentShadingRateClampCombinerInputs", &properties->fragmentShadingRateClampCombinerInputs) && - visitor->Visit("defaultRobustnessStorageBuffers", &properties->defaultRobustnessStorageBuffers) && - visitor->Visit("defaultRobustnessUniformBuffers", &properties->defaultRobustnessUniformBuffers) && - visitor->Visit("defaultRobustnessVertexInputs", &properties->defaultRobustnessVertexInputs) && - visitor->Visit("defaultRobustnessImages", &properties->defaultRobustnessImages) && - visitor->Visit("copySrcLayoutCount", &properties->copySrcLayoutCount) && - visitor->VisitArray("pCopySrcLayouts", properties->copySrcLayoutCount, &properties->pCopySrcLayouts) && - visitor->Visit("copyDstLayoutCount", &properties->copyDstLayoutCount) && - visitor->VisitArray("pCopyDstLayouts", properties->copyDstLayoutCount, &properties->pCopyDstLayouts) && - visitor->Visit("optimalTilingLayoutUUID", &properties->optimalTilingLayoutUUID) && - visitor->Visit("identicalMemoryTypeRequirements", &properties->identicalMemoryTypeRequirements); -} - -template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan14Features* features) { + + visitor->Visit("linearTilingFeatures", + &properties->linearTilingFeatures) && + visitor->Visit("optimalTilingFeatures", + &properties->optimalTilingFeatures) && + visitor->Visit("bufferFeatures", &properties->bufferFeatures); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceVariablePointersFeatures* features) { return - visitor->Visit("globalPriorityQuery", &features->globalPriorityQuery) && - visitor->Visit("shaderSubgroupRotate", &features->shaderSubgroupRotate) && - visitor->Visit("shaderSubgroupRotateClustered", &features->shaderSubgroupRotateClustered) && - visitor->Visit("shaderFloatControls2", &features->shaderFloatControls2) && - visitor->Visit("shaderExpectAssume", &features->shaderExpectAssume) && - visitor->Visit("rectangularLines", &features->rectangularLines) && - visitor->Visit("bresenhamLines", &features->bresenhamLines) && - visitor->Visit("smoothLines", &features->smoothLines) && - visitor->Visit("stippledRectangularLines", &features->stippledRectangularLines) && - visitor->Visit("stippledBresenhamLines", &features->stippledBresenhamLines) && - visitor->Visit("stippledSmoothLines", &features->stippledSmoothLines) && - visitor->Visit("vertexAttributeInstanceRateDivisor", &features->vertexAttributeInstanceRateDivisor) && - visitor->Visit("vertexAttributeInstanceRateZeroDivisor", &features->vertexAttributeInstanceRateZeroDivisor) && - visitor->Visit("indexTypeUint8", &features->indexTypeUint8) && - visitor->Visit("dynamicRenderingLocalRead", &features->dynamicRenderingLocalRead) && - visitor->Visit("maintenance5", &features->maintenance5) && - visitor->Visit("maintenance6", &features->maintenance6) && - visitor->Visit("pipelineProtectedAccess", &features->pipelineProtectedAccess) && - visitor->Visit("pipelineRobustness", &features->pipelineRobustness) && - visitor->Visit("hostImageCopy", &features->hostImageCopy); -} -// clang-format on + + visitor->Visit("variablePointersStorageBuffer", + &features->variablePointersStorageBuffer) && + visitor->Visit("variablePointers", &features->variablePointers); +} template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkJsonExtDriverProperties* properties) { - return visitor->Visit("driverPropertiesKHR", - &properties->driver_properties_khr); + VkPhysicalDeviceShaderFloat16Int8Features* features) { + return + + visitor->Visit("shaderFloat16", &features->shaderFloat16) && + visitor->Visit("shaderInt8", &features->shaderInt8); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceDriverPropertiesKHR* properties) { - return visitor->Visit("driverID", &properties->driverID) && - visitor->Visit("driverName", &properties->driverName) && - visitor->Visit("driverInfo", &properties->driverInfo) && - visitor->Visit("conformanceVersion", &properties->conformanceVersion); + VkPhysicalDeviceImage2DViewOf3DFeaturesEXT* features) { + return + + visitor->Visit("image2DViewOf3D", &features->image2DViewOf3D) && + visitor->Visit("sampler2DViewOf3D", &features->sampler2DViewOf3D); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkConformanceVersionKHR* version) { - return visitor->Visit("major", &version->major) && - visitor->Visit("minor", &version->minor) && - visitor->Visit("subminor", &version->subminor) && - visitor->Visit("patch", &version->patch); + VkPhysicalDeviceCustomBorderColorFeaturesEXT* features) { + return + + visitor->Visit("customBorderColors", &features->customBorderColors) && + visitor->Visit("customBorderColorWithoutFormat", + &features->customBorderColorWithoutFormat); +} + +template <typename Visitor> +inline bool Iterate( + Visitor* visitor, + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT* features) { + return + + visitor->Visit("primitiveTopologyListRestart", + &features->primitiveTopologyListRestart) && + visitor->Visit("primitiveTopologyPatchListRestart", + &features->primitiveTopologyPatchListRestart); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkJsonExtVariablePointerFeatures* features) { - return visitor->Visit("variablePointerFeaturesKHR", - &features->variable_pointer_features_khr); + VkPhysicalDeviceProvokingVertexFeaturesEXT* features) { + return + + visitor->Visit("provokingVertexLast", &features->provokingVertexLast) && + visitor->Visit("transformFeedbackPreservesProvokingVertex", + &features->transformFeedbackPreservesProvokingVertex); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkJsonExtShaderFloat16Int8Features* features) { - return visitor->Visit("shaderFloat16Int8FeaturesKHR", - &features->shader_float16_int8_features_khr); + VkPhysicalDeviceIndexTypeUint8Features* features) { + return + + visitor->Visit("indexTypeUint8", &features->indexTypeUint8); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkMemoryType* type) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceVertexAttributeDivisorFeatures* features) { return - visitor->Visit("propertyFlags", &type->propertyFlags) && - visitor->Visit("heapIndex", &type->heapIndex); + + visitor->Visit("vertexAttributeInstanceRateDivisor", + &features->vertexAttributeInstanceRateDivisor) && + visitor->Visit("vertexAttributeInstanceRateZeroDivisor", + &features->vertexAttributeInstanceRateZeroDivisor); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkMemoryHeap* heap) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceTransformFeedbackFeaturesEXT* features) { return - visitor->Visit("size", &heap->size) && - visitor->Visit("flags", &heap->flags); + + visitor->Visit("transformFeedback", &features->transformFeedback) && + visitor->Visit("geometryStreams", &features->geometryStreams); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkPhysicalDeviceMemoryProperties* properties) { +inline bool Iterate( + Visitor* visitor, + VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR* features) { return - visitor->Visit("memoryTypeCount", &properties->memoryTypeCount) && - visitor->VisitArray("memoryTypes", properties->memoryTypeCount, &properties->memoryTypes) && - visitor->Visit("memoryHeapCount", &properties->memoryHeapCount) && - visitor->VisitArray("memoryHeaps", properties->memoryHeapCount, &properties->memoryHeaps); + + visitor->Visit("shaderSubgroupUniformControlFlow", + &features->shaderSubgroupUniformControlFlow); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceSubgroupProperties* properties) { - return visitor->Visit("subgroupSize", &properties->subgroupSize) && - visitor->Visit("supportedStages", &properties->supportedStages) && - visitor->Visit("supportedOperations", - &properties->supportedOperations) && - visitor->Visit("quadOperationsInAllStages", - &properties->quadOperationsInAllStages); +inline bool Iterate( + Visitor* visitor, + VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures* features) { + return + + visitor->Visit("shaderSubgroupExtendedTypes", + &features->shaderSubgroupExtendedTypes); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDevicePointClippingProperties* properties) { - return visitor->Visit("pointClippingBehavior", - &properties->pointClippingBehavior); + VkPhysicalDevice8BitStorageFeatures* features) { + return + + visitor->Visit("storageBuffer8BitAccess", + &features->storageBuffer8BitAccess) && + visitor->Visit("uniformAndStorageBuffer8BitAccess", + &features->uniformAndStorageBuffer8BitAccess) && + visitor->Visit("storagePushConstant8", &features->storagePushConstant8); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceMultiviewProperties* properties) { - return visitor->Visit("maxMultiviewViewCount", - &properties->maxMultiviewViewCount) && - visitor->Visit("maxMultiviewInstanceIndex", - &properties->maxMultiviewInstanceIndex); + VkPhysicalDeviceShaderIntegerDotProductFeatures* features) { + return + + visitor->Visit("shaderIntegerDotProduct", + &features->shaderIntegerDotProduct); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceIDProperties* properties) { - return visitor->Visit("deviceUUID", &properties->deviceUUID) && - visitor->Visit("driverUUID", &properties->driverUUID) && - visitor->Visit("deviceLUID", &properties->deviceLUID) && - visitor->Visit("deviceNodeMask", &properties->deviceNodeMask) && - visitor->Visit("deviceLUIDValid", &properties->deviceLUIDValid); +inline bool Iterate( + Visitor* visitor, + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG* features) { + return + + visitor->Visit("relaxedLineRasterization", + &features->relaxedLineRasterization); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceMaintenance3Properties* properties) { - return visitor->Visit("maxPerSetDescriptors", - &properties->maxPerSetDescriptors) && - visitor->Visit("maxMemoryAllocationSize", - &properties->maxMemoryAllocationSize); + VkPhysicalDeviceLineRasterizationFeatures* features) { + return + + visitor->Visit("rectangularLines", &features->rectangularLines) && + visitor->Visit("bresenhamLines", &features->bresenhamLines) && + visitor->Visit("smoothLines", &features->smoothLines) && + visitor->Visit("stippledRectangularLines", + &features->stippledRectangularLines) && + visitor->Visit("stippledBresenhamLines", + &features->stippledBresenhamLines) && + visitor->Visit("stippledSmoothLines", &features->stippledSmoothLines); +} + +template <typename Visitor> +inline bool Iterate( + Visitor* visitor, + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT* features) { + return + + visitor->Visit("primitivesGeneratedQuery", + &features->primitivesGeneratedQuery) && + visitor->Visit( + "primitivesGeneratedQueryWithRasterizerDiscard", + &features->primitivesGeneratedQueryWithRasterizerDiscard) && + visitor->Visit("primitivesGeneratedQueryWithNonZeroStreams", + &features->primitivesGeneratedQueryWithNonZeroStreams); } template <typename Visitor> inline bool Iterate(Visitor* visitor, VkPhysicalDevice16BitStorageFeatures* features) { - return visitor->Visit("storageBuffer16BitAccess", - &features->storageBuffer16BitAccess) && - visitor->Visit("uniformAndStorageBuffer16BitAccess", - &features->uniformAndStorageBuffer16BitAccess) && - visitor->Visit("storagePushConstant16", - &features->storagePushConstant16) && - visitor->Visit("storageInputOutput16", - &features->storageInputOutput16); + return + + visitor->Visit("storageBuffer16BitAccess", + &features->storageBuffer16BitAccess) && + visitor->Visit("uniformAndStorageBuffer16BitAccess", + &features->uniformAndStorageBuffer16BitAccess) && + visitor->Visit("storagePushConstant16", + &features->storagePushConstant16) && + visitor->Visit("storageInputOutput16", &features->storageInputOutput16); } template <typename Visitor> inline bool Iterate(Visitor* visitor, VkPhysicalDeviceMultiviewFeatures* features) { - return visitor->Visit("multiview", &features->multiview) && - visitor->Visit("multiviewGeometryShader", - &features->multiviewGeometryShader) && - visitor->Visit("multiviewTessellationShader", - &features->multiviewTessellationShader); + return + + visitor->Visit("multiview", &features->multiview) && + visitor->Visit("multiviewGeometryShader", + &features->multiviewGeometryShader) && + visitor->Visit("multiviewTessellationShader", + &features->multiviewTessellationShader); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceVariablePointerFeatures* features) { - return visitor->Visit("variablePointersStorageBuffer", - &features->variablePointersStorageBuffer) && - visitor->Visit("variablePointers", &features->variablePointers); + VkPhysicalDeviceProtectedMemoryFeatures* features) { + return + + visitor->Visit("protectedMemory", &features->protectedMemory); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceShaderFloat16Int8FeaturesKHR* features) { - return visitor->Visit("shaderFloat16", &features->shaderFloat16) && - visitor->Visit("shaderInt8", &features->shaderInt8); + VkPhysicalDeviceSamplerYcbcrConversionFeatures* features) { + return + + visitor->Visit("samplerYcbcrConversion", + &features->samplerYcbcrConversion); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceProtectedMemoryFeatures* features) { - return visitor->Visit("protectedMemory", &features->protectedMemory); + VkPhysicalDeviceShaderDrawParameterFeatures* features) { + return + + visitor->Visit("shaderDrawParameters", &features->shaderDrawParameters); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkPhysicalDeviceLimits* limits) { + return + + visitor->Visit("maxImageDimension1D", &limits->maxImageDimension1D) && + visitor->Visit("maxImageDimension2D", &limits->maxImageDimension2D) && + visitor->Visit("maxImageDimension3D", &limits->maxImageDimension3D) && + visitor->Visit("maxImageDimensionCube", &limits->maxImageDimensionCube) && + visitor->Visit("maxImageArrayLayers", &limits->maxImageArrayLayers) && + visitor->Visit("maxTexelBufferElements", + &limits->maxTexelBufferElements) && + visitor->Visit("maxUniformBufferRange", &limits->maxUniformBufferRange) && + visitor->Visit("maxStorageBufferRange", &limits->maxStorageBufferRange) && + visitor->Visit("maxPushConstantsSize", &limits->maxPushConstantsSize) && + visitor->Visit("maxMemoryAllocationCount", + &limits->maxMemoryAllocationCount) && + visitor->Visit("maxSamplerAllocationCount", + &limits->maxSamplerAllocationCount) && + visitor->Visit("bufferImageGranularity", + &limits->bufferImageGranularity) && + visitor->Visit("sparseAddressSpaceSize", + &limits->sparseAddressSpaceSize) && + visitor->Visit("maxBoundDescriptorSets", + &limits->maxBoundDescriptorSets) && + visitor->Visit("maxPerStageDescriptorSamplers", + &limits->maxPerStageDescriptorSamplers) && + visitor->Visit("maxPerStageDescriptorUniformBuffers", + &limits->maxPerStageDescriptorUniformBuffers) && + visitor->Visit("maxPerStageDescriptorStorageBuffers", + &limits->maxPerStageDescriptorStorageBuffers) && + visitor->Visit("maxPerStageDescriptorSampledImages", + &limits->maxPerStageDescriptorSampledImages) && + visitor->Visit("maxPerStageDescriptorStorageImages", + &limits->maxPerStageDescriptorStorageImages) && + visitor->Visit("maxPerStageDescriptorInputAttachments", + &limits->maxPerStageDescriptorInputAttachments) && + visitor->Visit("maxPerStageResources", &limits->maxPerStageResources) && + visitor->Visit("maxDescriptorSetSamplers", + &limits->maxDescriptorSetSamplers) && + visitor->Visit("maxDescriptorSetUniformBuffers", + &limits->maxDescriptorSetUniformBuffers) && + visitor->Visit("maxDescriptorSetUniformBuffersDynamic", + &limits->maxDescriptorSetUniformBuffersDynamic) && + visitor->Visit("maxDescriptorSetStorageBuffers", + &limits->maxDescriptorSetStorageBuffers) && + visitor->Visit("maxDescriptorSetStorageBuffersDynamic", + &limits->maxDescriptorSetStorageBuffersDynamic) && + visitor->Visit("maxDescriptorSetSampledImages", + &limits->maxDescriptorSetSampledImages) && + visitor->Visit("maxDescriptorSetStorageImages", + &limits->maxDescriptorSetStorageImages) && + visitor->Visit("maxDescriptorSetInputAttachments", + &limits->maxDescriptorSetInputAttachments) && + visitor->Visit("maxVertexInputAttributes", + &limits->maxVertexInputAttributes) && + visitor->Visit("maxVertexInputBindings", + &limits->maxVertexInputBindings) && + visitor->Visit("maxVertexInputAttributeOffset", + &limits->maxVertexInputAttributeOffset) && + visitor->Visit("maxVertexInputBindingStride", + &limits->maxVertexInputBindingStride) && + visitor->Visit("maxVertexOutputComponents", + &limits->maxVertexOutputComponents) && + visitor->Visit("maxTessellationGenerationLevel", + &limits->maxTessellationGenerationLevel) && + visitor->Visit("maxTessellationPatchSize", + &limits->maxTessellationPatchSize) && + visitor->Visit("maxTessellationControlPerVertexInputComponents", + &limits->maxTessellationControlPerVertexInputComponents) && + visitor->Visit( + "maxTessellationControlPerVertexOutputComponents", + &limits->maxTessellationControlPerVertexOutputComponents) && + visitor->Visit("maxTessellationControlPerPatchOutputComponents", + &limits->maxTessellationControlPerPatchOutputComponents) && + visitor->Visit("maxTessellationControlTotalOutputComponents", + &limits->maxTessellationControlTotalOutputComponents) && + visitor->Visit("maxTessellationEvaluationInputComponents", + &limits->maxTessellationEvaluationInputComponents) && + visitor->Visit("maxTessellationEvaluationOutputComponents", + &limits->maxTessellationEvaluationOutputComponents) && + visitor->Visit("maxGeometryShaderInvocations", + &limits->maxGeometryShaderInvocations) && + visitor->Visit("maxGeometryInputComponents", + &limits->maxGeometryInputComponents) && + visitor->Visit("maxGeometryOutputComponents", + &limits->maxGeometryOutputComponents) && + visitor->Visit("maxGeometryOutputVertices", + &limits->maxGeometryOutputVertices) && + visitor->Visit("maxGeometryTotalOutputComponents", + &limits->maxGeometryTotalOutputComponents) && + visitor->Visit("maxFragmentInputComponents", + &limits->maxFragmentInputComponents) && + visitor->Visit("maxFragmentOutputAttachments", + &limits->maxFragmentOutputAttachments) && + visitor->Visit("maxFragmentDualSrcAttachments", + &limits->maxFragmentDualSrcAttachments) && + visitor->Visit("maxFragmentCombinedOutputResources", + &limits->maxFragmentCombinedOutputResources) && + visitor->Visit("maxComputeSharedMemorySize", + &limits->maxComputeSharedMemorySize) && + visitor->Visit("maxComputeWorkGroupCount", + &limits->maxComputeWorkGroupCount) && + visitor->Visit("maxComputeWorkGroupInvocations", + &limits->maxComputeWorkGroupInvocations) && + visitor->Visit("maxComputeWorkGroupSize", + &limits->maxComputeWorkGroupSize) && + visitor->Visit("subPixelPrecisionBits", &limits->subPixelPrecisionBits) && + visitor->Visit("subTexelPrecisionBits", &limits->subTexelPrecisionBits) && + visitor->Visit("mipmapPrecisionBits", &limits->mipmapPrecisionBits) && + visitor->Visit("maxDrawIndexedIndexValue", + &limits->maxDrawIndexedIndexValue) && + visitor->Visit("maxDrawIndirectCount", &limits->maxDrawIndirectCount) && + visitor->Visit("maxSamplerLodBias", &limits->maxSamplerLodBias) && + visitor->Visit("maxSamplerAnisotropy", &limits->maxSamplerAnisotropy) && + visitor->Visit("maxViewports", &limits->maxViewports) && + visitor->Visit("maxViewportDimensions", &limits->maxViewportDimensions) && + visitor->Visit("viewportBoundsRange", &limits->viewportBoundsRange) && + visitor->Visit("viewportSubPixelBits", &limits->viewportSubPixelBits) && + visitor->Visit("minMemoryMapAlignment", &limits->minMemoryMapAlignment) && + visitor->Visit("minTexelBufferOffsetAlignment", + &limits->minTexelBufferOffsetAlignment) && + visitor->Visit("minUniformBufferOffsetAlignment", + &limits->minUniformBufferOffsetAlignment) && + visitor->Visit("minStorageBufferOffsetAlignment", + &limits->minStorageBufferOffsetAlignment) && + visitor->Visit("minTexelOffset", &limits->minTexelOffset) && + visitor->Visit("maxTexelOffset", &limits->maxTexelOffset) && + visitor->Visit("minTexelGatherOffset", &limits->minTexelGatherOffset) && + visitor->Visit("maxTexelGatherOffset", &limits->maxTexelGatherOffset) && + visitor->Visit("minInterpolationOffset", + &limits->minInterpolationOffset) && + visitor->Visit("maxInterpolationOffset", + &limits->maxInterpolationOffset) && + visitor->Visit("subPixelInterpolationOffsetBits", + &limits->subPixelInterpolationOffsetBits) && + visitor->Visit("maxFramebufferWidth", &limits->maxFramebufferWidth) && + visitor->Visit("maxFramebufferHeight", &limits->maxFramebufferHeight) && + visitor->Visit("maxFramebufferLayers", &limits->maxFramebufferLayers) && + visitor->Visit("framebufferColorSampleCounts", + &limits->framebufferColorSampleCounts) && + visitor->Visit("framebufferDepthSampleCounts", + &limits->framebufferDepthSampleCounts) && + visitor->Visit("framebufferStencilSampleCounts", + &limits->framebufferStencilSampleCounts) && + visitor->Visit("framebufferNoAttachmentsSampleCounts", + &limits->framebufferNoAttachmentsSampleCounts) && + visitor->Visit("maxColorAttachments", &limits->maxColorAttachments) && + visitor->Visit("sampledImageColorSampleCounts", + &limits->sampledImageColorSampleCounts) && + visitor->Visit("sampledImageIntegerSampleCounts", + &limits->sampledImageIntegerSampleCounts) && + visitor->Visit("sampledImageDepthSampleCounts", + &limits->sampledImageDepthSampleCounts) && + visitor->Visit("sampledImageStencilSampleCounts", + &limits->sampledImageStencilSampleCounts) && + visitor->Visit("storageImageSampleCounts", + &limits->storageImageSampleCounts) && + visitor->Visit("maxSampleMaskWords", &limits->maxSampleMaskWords) && + visitor->Visit("timestampComputeAndGraphics", + &limits->timestampComputeAndGraphics) && + visitor->Visit("timestampPeriod", &limits->timestampPeriod) && + visitor->Visit("maxClipDistances", &limits->maxClipDistances) && + visitor->Visit("maxCullDistances", &limits->maxCullDistances) && + visitor->Visit("maxCombinedClipAndCullDistances", + &limits->maxCombinedClipAndCullDistances) && + visitor->Visit("discreteQueuePriorities", + &limits->discreteQueuePriorities) && + visitor->Visit("pointSizeRange", &limits->pointSizeRange) && + visitor->Visit("lineWidthRange", &limits->lineWidthRange) && + visitor->Visit("pointSizeGranularity", &limits->pointSizeGranularity) && + visitor->Visit("lineWidthGranularity", &limits->lineWidthGranularity) && + visitor->Visit("strictLines", &limits->strictLines) && + visitor->Visit("standardSampleLocations", + &limits->standardSampleLocations) && + visitor->Visit("optimalBufferCopyOffsetAlignment", + &limits->optimalBufferCopyOffsetAlignment) && + visitor->Visit("optimalBufferCopyRowPitchAlignment", + &limits->optimalBufferCopyRowPitchAlignment) && + visitor->Visit("nonCoherentAtomSize", &limits->nonCoherentAtomSize); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkPhysicalDeviceFeatures* features) { + return + + visitor->Visit("robustBufferAccess", &features->robustBufferAccess) && + visitor->Visit("fullDrawIndexUint32", &features->fullDrawIndexUint32) && + visitor->Visit("imageCubeArray", &features->imageCubeArray) && + visitor->Visit("independentBlend", &features->independentBlend) && + visitor->Visit("geometryShader", &features->geometryShader) && + visitor->Visit("tessellationShader", &features->tessellationShader) && + visitor->Visit("sampleRateShading", &features->sampleRateShading) && + visitor->Visit("dualSrcBlend", &features->dualSrcBlend) && + visitor->Visit("logicOp", &features->logicOp) && + visitor->Visit("multiDrawIndirect", &features->multiDrawIndirect) && + visitor->Visit("drawIndirectFirstInstance", + &features->drawIndirectFirstInstance) && + visitor->Visit("depthClamp", &features->depthClamp) && + visitor->Visit("depthBiasClamp", &features->depthBiasClamp) && + visitor->Visit("fillModeNonSolid", &features->fillModeNonSolid) && + visitor->Visit("depthBounds", &features->depthBounds) && + visitor->Visit("wideLines", &features->wideLines) && + visitor->Visit("largePoints", &features->largePoints) && + visitor->Visit("alphaToOne", &features->alphaToOne) && + visitor->Visit("multiViewport", &features->multiViewport) && + visitor->Visit("samplerAnisotropy", &features->samplerAnisotropy) && + visitor->Visit("textureCompressionETC2", + &features->textureCompressionETC2) && + visitor->Visit("textureCompressionASTC_LDR", + &features->textureCompressionASTC_LDR) && + visitor->Visit("textureCompressionBC", &features->textureCompressionBC) && + visitor->Visit("occlusionQueryPrecise", + &features->occlusionQueryPrecise) && + visitor->Visit("pipelineStatisticsQuery", + &features->pipelineStatisticsQuery) && + visitor->Visit("vertexPipelineStoresAndAtomics", + &features->vertexPipelineStoresAndAtomics) && + visitor->Visit("fragmentStoresAndAtomics", + &features->fragmentStoresAndAtomics) && + visitor->Visit("shaderTessellationAndGeometryPointSize", + &features->shaderTessellationAndGeometryPointSize) && + visitor->Visit("shaderImageGatherExtended", + &features->shaderImageGatherExtended) && + visitor->Visit("shaderStorageImageExtendedFormats", + &features->shaderStorageImageExtendedFormats) && + visitor->Visit("shaderStorageImageMultisample", + &features->shaderStorageImageMultisample) && + visitor->Visit("shaderStorageImageReadWithoutFormat", + &features->shaderStorageImageReadWithoutFormat) && + visitor->Visit("shaderStorageImageWriteWithoutFormat", + &features->shaderStorageImageWriteWithoutFormat) && + visitor->Visit("shaderUniformBufferArrayDynamicIndexing", + &features->shaderUniformBufferArrayDynamicIndexing) && + visitor->Visit("shaderSampledImageArrayDynamicIndexing", + &features->shaderSampledImageArrayDynamicIndexing) && + visitor->Visit("shaderStorageBufferArrayDynamicIndexing", + &features->shaderStorageBufferArrayDynamicIndexing) && + visitor->Visit("shaderStorageImageArrayDynamicIndexing", + &features->shaderStorageImageArrayDynamicIndexing) && + visitor->Visit("shaderClipDistance", &features->shaderClipDistance) && + visitor->Visit("shaderCullDistance", &features->shaderCullDistance) && + visitor->Visit("shaderFloat64", &features->shaderFloat64) && + visitor->Visit("shaderInt64", &features->shaderInt64) && + visitor->Visit("shaderInt16", &features->shaderInt16) && + visitor->Visit("shaderResourceResidency", + &features->shaderResourceResidency) && + visitor->Visit("shaderResourceMinLod", &features->shaderResourceMinLod) && + visitor->Visit("sparseBinding", &features->sparseBinding) && + visitor->Visit("sparseResidencyBuffer", + &features->sparseResidencyBuffer) && + visitor->Visit("sparseResidencyImage2D", + &features->sparseResidencyImage2D) && + visitor->Visit("sparseResidencyImage3D", + &features->sparseResidencyImage3D) && + visitor->Visit("sparseResidency2Samples", + &features->sparseResidency2Samples) && + visitor->Visit("sparseResidency4Samples", + &features->sparseResidency4Samples) && + visitor->Visit("sparseResidency8Samples", + &features->sparseResidency8Samples) && + visitor->Visit("sparseResidency16Samples", + &features->sparseResidency16Samples) && + visitor->Visit("sparseResidencyAliased", + &features->sparseResidencyAliased) && + visitor->Visit("variableMultisampleRate", + &features->variableMultisampleRate) && + visitor->Visit("inheritedQueries", &features->inheritedQueries); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceSamplerYcbcrConversionFeatures* features) { - return visitor->Visit("samplerYcbcrConversion", - &features->samplerYcbcrConversion); + VkPhysicalDeviceVulkan11Properties* properties) { + return + + visitor->Visit("deviceUUID", &properties->deviceUUID) && + visitor->Visit("driverUUID", &properties->driverUUID) && + visitor->Visit("deviceLUID", &properties->deviceLUID) && + visitor->Visit("deviceNodeMask", &properties->deviceNodeMask) && + visitor->Visit("deviceLUIDValid", &properties->deviceLUIDValid) && + visitor->Visit("subgroupSize", &properties->subgroupSize) && + visitor->Visit("subgroupSupportedStages", + &properties->subgroupSupportedStages) && + visitor->Visit("subgroupSupportedOperations", + &properties->subgroupSupportedOperations) && + visitor->Visit("subgroupQuadOperationsInAllStages", + &properties->subgroupQuadOperationsInAllStages) && + visitor->Visit("pointClippingBehavior", + &properties->pointClippingBehavior) && + visitor->Visit("maxMultiviewViewCount", + &properties->maxMultiviewViewCount) && + visitor->Visit("maxMultiviewInstanceIndex", + &properties->maxMultiviewInstanceIndex) && + visitor->Visit("protectedNoFault", &properties->protectedNoFault) && + visitor->Visit("maxPerSetDescriptors", + &properties->maxPerSetDescriptors) && + visitor->Visit("maxMemoryAllocationSize", + &properties->maxMemoryAllocationSize); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkPhysicalDeviceShaderDrawParameterFeatures* features) { - return visitor->Visit("shaderDrawParameters", - &features->shaderDrawParameters); + VkPhysicalDeviceVulkan11Features* features) { + return + + visitor->Visit("storageBuffer16BitAccess", + &features->storageBuffer16BitAccess) && + visitor->Visit("uniformAndStorageBuffer16BitAccess", + &features->uniformAndStorageBuffer16BitAccess) && + visitor->Visit("storagePushConstant16", + &features->storagePushConstant16) && + visitor->Visit("storageInputOutput16", &features->storageInputOutput16) && + visitor->Visit("multiview", &features->multiview) && + visitor->Visit("multiviewGeometryShader", + &features->multiviewGeometryShader) && + visitor->Visit("multiviewTessellationShader", + &features->multiviewTessellationShader) && + visitor->Visit("variablePointersStorageBuffer", + &features->variablePointersStorageBuffer) && + visitor->Visit("variablePointers", &features->variablePointers) && + visitor->Visit("protectedMemory", &features->protectedMemory) && + visitor->Visit("samplerYcbcrConversion", + &features->samplerYcbcrConversion) && + visitor->Visit("shaderDrawParameters", &features->shaderDrawParameters); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkExternalFenceProperties* properties) { - return visitor->Visit("exportFromImportedHandleTypes", - &properties->exportFromImportedHandleTypes) && - visitor->Visit("compatibleHandleTypes", - &properties->compatibleHandleTypes) && - visitor->Visit("externalFenceFeatures", - &properties->externalFenceFeatures); +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceVulkan12Properties* properties) { + return + + visitor->Visit("driverID", &properties->driverID) && + visitor->Visit("driverName", &properties->driverName) && + visitor->Visit("driverInfo", &properties->driverInfo) && + visitor->Visit("conformanceVersion", &properties->conformanceVersion) && + visitor->Visit("denormBehaviorIndependence", + &properties->denormBehaviorIndependence) && + visitor->Visit("roundingModeIndependence", + &properties->roundingModeIndependence) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat16", + &properties->shaderSignedZeroInfNanPreserveFloat16) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat32", + &properties->shaderSignedZeroInfNanPreserveFloat32) && + visitor->Visit("shaderSignedZeroInfNanPreserveFloat64", + &properties->shaderSignedZeroInfNanPreserveFloat64) && + visitor->Visit("shaderDenormPreserveFloat16", + &properties->shaderDenormPreserveFloat16) && + visitor->Visit("shaderDenormPreserveFloat32", + &properties->shaderDenormPreserveFloat32) && + visitor->Visit("shaderDenormPreserveFloat64", + &properties->shaderDenormPreserveFloat64) && + visitor->Visit("shaderDenormFlushToZeroFloat16", + &properties->shaderDenormFlushToZeroFloat16) && + visitor->Visit("shaderDenormFlushToZeroFloat32", + &properties->shaderDenormFlushToZeroFloat32) && + visitor->Visit("shaderDenormFlushToZeroFloat64", + &properties->shaderDenormFlushToZeroFloat64) && + visitor->Visit("shaderRoundingModeRTEFloat16", + &properties->shaderRoundingModeRTEFloat16) && + visitor->Visit("shaderRoundingModeRTEFloat32", + &properties->shaderRoundingModeRTEFloat32) && + visitor->Visit("shaderRoundingModeRTEFloat64", + &properties->shaderRoundingModeRTEFloat64) && + visitor->Visit("shaderRoundingModeRTZFloat16", + &properties->shaderRoundingModeRTZFloat16) && + visitor->Visit("shaderRoundingModeRTZFloat32", + &properties->shaderRoundingModeRTZFloat32) && + visitor->Visit("shaderRoundingModeRTZFloat64", + &properties->shaderRoundingModeRTZFloat64) && + visitor->Visit("maxUpdateAfterBindDescriptorsInAllPools", + &properties->maxUpdateAfterBindDescriptorsInAllPools) && + visitor->Visit( + "shaderUniformBufferArrayNonUniformIndexingNative", + &properties->shaderUniformBufferArrayNonUniformIndexingNative) && + visitor->Visit( + "shaderSampledImageArrayNonUniformIndexingNative", + &properties->shaderSampledImageArrayNonUniformIndexingNative) && + visitor->Visit( + "shaderStorageBufferArrayNonUniformIndexingNative", + &properties->shaderStorageBufferArrayNonUniformIndexingNative) && + visitor->Visit( + "shaderStorageImageArrayNonUniformIndexingNative", + &properties->shaderStorageImageArrayNonUniformIndexingNative) && + visitor->Visit( + "shaderInputAttachmentArrayNonUniformIndexingNative", + &properties->shaderInputAttachmentArrayNonUniformIndexingNative) && + visitor->Visit("robustBufferAccessUpdateAfterBind", + &properties->robustBufferAccessUpdateAfterBind) && + visitor->Visit("quadDivergentImplicitLod", + &properties->quadDivergentImplicitLod) && + visitor->Visit( + "maxPerStageDescriptorUpdateAfterBindSamplers", + &properties->maxPerStageDescriptorUpdateAfterBindSamplers) && + visitor->Visit( + "maxPerStageDescriptorUpdateAfterBindUniformBuffers", + &properties->maxPerStageDescriptorUpdateAfterBindUniformBuffers) && + visitor->Visit( + "maxPerStageDescriptorUpdateAfterBindStorageBuffers", + &properties->maxPerStageDescriptorUpdateAfterBindStorageBuffers) && + visitor->Visit( + "maxPerStageDescriptorUpdateAfterBindSampledImages", + &properties->maxPerStageDescriptorUpdateAfterBindSampledImages) && + visitor->Visit( + "maxPerStageDescriptorUpdateAfterBindStorageImages", + &properties->maxPerStageDescriptorUpdateAfterBindStorageImages) && + visitor->Visit( + "maxPerStageDescriptorUpdateAfterBindInputAttachments", + &properties->maxPerStageDescriptorUpdateAfterBindInputAttachments) && + visitor->Visit("maxPerStageUpdateAfterBindResources", + &properties->maxPerStageUpdateAfterBindResources) && + visitor->Visit("maxDescriptorSetUpdateAfterBindSamplers", + &properties->maxDescriptorSetUpdateAfterBindSamplers) && + visitor->Visit( + "maxDescriptorSetUpdateAfterBindUniformBuffers", + &properties->maxDescriptorSetUpdateAfterBindUniformBuffers) && + visitor->Visit( + "maxDescriptorSetUpdateAfterBindUniformBuffersDynamic", + &properties->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic) && + visitor->Visit( + "maxDescriptorSetUpdateAfterBindStorageBuffers", + &properties->maxDescriptorSetUpdateAfterBindStorageBuffers) && + visitor->Visit( + "maxDescriptorSetUpdateAfterBindStorageBuffersDynamic", + &properties->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic) && + visitor->Visit( + "maxDescriptorSetUpdateAfterBindSampledImages", + &properties->maxDescriptorSetUpdateAfterBindSampledImages) && + visitor->Visit( + "maxDescriptorSetUpdateAfterBindStorageImages", + &properties->maxDescriptorSetUpdateAfterBindStorageImages) && + visitor->Visit( + "maxDescriptorSetUpdateAfterBindInputAttachments", + &properties->maxDescriptorSetUpdateAfterBindInputAttachments) && + visitor->Visit("supportedDepthResolveModes", + &properties->supportedDepthResolveModes) && + visitor->Visit("supportedStencilResolveModes", + &properties->supportedStencilResolveModes) && + visitor->Visit("independentResolveNone", + &properties->independentResolveNone) && + visitor->Visit("independentResolve", &properties->independentResolve) && + visitor->Visit("filterMinmaxSingleComponentFormats", + &properties->filterMinmaxSingleComponentFormats) && + visitor->Visit("filterMinmaxImageComponentMapping", + &properties->filterMinmaxImageComponentMapping) && + visitor->Visit("maxTimelineSemaphoreValueDifference", + &properties->maxTimelineSemaphoreValueDifference) && + visitor->Visit("framebufferIntegerColorSampleCounts", + &properties->framebufferIntegerColorSampleCounts); } template <typename Visitor> inline bool Iterate(Visitor* visitor, - VkExternalSemaphoreProperties* properties) { - return visitor->Visit("exportFromImportedHandleTypes", - &properties->exportFromImportedHandleTypes) && - visitor->Visit("compatibleHandleTypes", - &properties->compatibleHandleTypes) && - visitor->Visit("externalSemaphoreFeatures", - &properties->externalSemaphoreFeatures); + VkPhysicalDeviceVulkan12Features* features) { + return + + visitor->Visit("samplerMirrorClampToEdge", + &features->samplerMirrorClampToEdge) && + visitor->Visit("drawIndirectCount", &features->drawIndirectCount) && + visitor->Visit("storageBuffer8BitAccess", + &features->storageBuffer8BitAccess) && + visitor->Visit("uniformAndStorageBuffer8BitAccess", + &features->uniformAndStorageBuffer8BitAccess) && + visitor->Visit("storagePushConstant8", &features->storagePushConstant8) && + visitor->Visit("shaderBufferInt64Atomics", + &features->shaderBufferInt64Atomics) && + visitor->Visit("shaderSharedInt64Atomics", + &features->shaderSharedInt64Atomics) && + visitor->Visit("shaderFloat16", &features->shaderFloat16) && + visitor->Visit("shaderInt8", &features->shaderInt8) && + visitor->Visit("descriptorIndexing", &features->descriptorIndexing) && + visitor->Visit("shaderInputAttachmentArrayDynamicIndexing", + &features->shaderInputAttachmentArrayDynamicIndexing) && + visitor->Visit("shaderUniformTexelBufferArrayDynamicIndexing", + &features->shaderUniformTexelBufferArrayDynamicIndexing) && + visitor->Visit("shaderStorageTexelBufferArrayDynamicIndexing", + &features->shaderStorageTexelBufferArrayDynamicIndexing) && + visitor->Visit("shaderUniformBufferArrayNonUniformIndexing", + &features->shaderUniformBufferArrayNonUniformIndexing) && + visitor->Visit("shaderSampledImageArrayNonUniformIndexing", + &features->shaderSampledImageArrayNonUniformIndexing) && + visitor->Visit("shaderStorageBufferArrayNonUniformIndexing", + &features->shaderStorageBufferArrayNonUniformIndexing) && + visitor->Visit("shaderStorageImageArrayNonUniformIndexing", + &features->shaderStorageImageArrayNonUniformIndexing) && + visitor->Visit("shaderInputAttachmentArrayNonUniformIndexing", + &features->shaderInputAttachmentArrayNonUniformIndexing) && + visitor->Visit( + "shaderUniformTexelBufferArrayNonUniformIndexing", + &features->shaderUniformTexelBufferArrayNonUniformIndexing) && + visitor->Visit( + "shaderStorageTexelBufferArrayNonUniformIndexing", + &features->shaderStorageTexelBufferArrayNonUniformIndexing) && + visitor->Visit( + "descriptorBindingUniformBufferUpdateAfterBind", + &features->descriptorBindingUniformBufferUpdateAfterBind) && + visitor->Visit("descriptorBindingSampledImageUpdateAfterBind", + &features->descriptorBindingSampledImageUpdateAfterBind) && + visitor->Visit("descriptorBindingStorageImageUpdateAfterBind", + &features->descriptorBindingStorageImageUpdateAfterBind) && + visitor->Visit( + "descriptorBindingStorageBufferUpdateAfterBind", + &features->descriptorBindingStorageBufferUpdateAfterBind) && + visitor->Visit( + "descriptorBindingUniformTexelBufferUpdateAfterBind", + &features->descriptorBindingUniformTexelBufferUpdateAfterBind) && + visitor->Visit( + "descriptorBindingStorageTexelBufferUpdateAfterBind", + &features->descriptorBindingStorageTexelBufferUpdateAfterBind) && + visitor->Visit("descriptorBindingUpdateUnusedWhilePending", + &features->descriptorBindingUpdateUnusedWhilePending) && + visitor->Visit("descriptorBindingPartiallyBound", + &features->descriptorBindingPartiallyBound) && + visitor->Visit("descriptorBindingVariableDescriptorCount", + &features->descriptorBindingVariableDescriptorCount) && + visitor->Visit("runtimeDescriptorArray", + &features->runtimeDescriptorArray) && + visitor->Visit("samplerFilterMinmax", &features->samplerFilterMinmax) && + visitor->Visit("scalarBlockLayout", &features->scalarBlockLayout) && + visitor->Visit("imagelessFramebuffer", &features->imagelessFramebuffer) && + visitor->Visit("uniformBufferStandardLayout", + &features->uniformBufferStandardLayout) && + visitor->Visit("shaderSubgroupExtendedTypes", + &features->shaderSubgroupExtendedTypes) && + visitor->Visit("separateDepthStencilLayouts", + &features->separateDepthStencilLayouts) && + visitor->Visit("hostQueryReset", &features->hostQueryReset) && + visitor->Visit("timelineSemaphore", &features->timelineSemaphore) && + visitor->Visit("bufferDeviceAddress", &features->bufferDeviceAddress) && + visitor->Visit("bufferDeviceAddressCaptureReplay", + &features->bufferDeviceAddressCaptureReplay) && + visitor->Visit("bufferDeviceAddressMultiDevice", + &features->bufferDeviceAddressMultiDevice) && + visitor->Visit("vulkanMemoryModel", &features->vulkanMemoryModel) && + visitor->Visit("vulkanMemoryModelDeviceScope", + &features->vulkanMemoryModelDeviceScope) && + visitor->Visit( + "vulkanMemoryModelAvailabilityVisibilityChains", + &features->vulkanMemoryModelAvailabilityVisibilityChains) && + visitor->Visit("shaderOutputViewportIndex", + &features->shaderOutputViewportIndex) && + visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) && + visitor->Visit("subgroupBroadcastDynamicId", + &features->subgroupBroadcastDynamicId); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkQueueFamilyProperties* properties) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceVulkan13Properties* properties) { return - visitor->Visit("queueFlags", &properties->queueFlags) && - visitor->Visit("queueCount", &properties->queueCount) && - visitor->Visit("timestampValidBits", &properties->timestampValidBits) && - visitor->Visit("minImageTransferGranularity", &properties->minImageTransferGranularity); + + visitor->Visit("minSubgroupSize", &properties->minSubgroupSize) && + visitor->Visit("maxSubgroupSize", &properties->maxSubgroupSize) && + visitor->Visit("maxComputeWorkgroupSubgroups", + &properties->maxComputeWorkgroupSubgroups) && + visitor->Visit("requiredSubgroupSizeStages", + &properties->requiredSubgroupSizeStages) && + visitor->Visit("maxInlineUniformBlockSize", + &properties->maxInlineUniformBlockSize) && + visitor->Visit("maxPerStageDescriptorInlineUniformBlocks", + &properties->maxPerStageDescriptorInlineUniformBlocks) && + visitor->Visit( + "maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks", + &properties + ->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks) && + visitor->Visit("maxDescriptorSetInlineUniformBlocks", + &properties->maxDescriptorSetInlineUniformBlocks) && + visitor->Visit( + "maxDescriptorSetUpdateAfterBindInlineUniformBlocks", + &properties->maxDescriptorSetUpdateAfterBindInlineUniformBlocks) && + visitor->Visit("maxInlineUniformTotalSize", + &properties->maxInlineUniformTotalSize) && + visitor->Visit("integerDotProduct8BitUnsignedAccelerated", + &properties->integerDotProduct8BitUnsignedAccelerated) && + visitor->Visit("integerDotProduct8BitSignedAccelerated", + &properties->integerDotProduct8BitSignedAccelerated) && + visitor->Visit( + "integerDotProduct8BitMixedSignednessAccelerated", + &properties->integerDotProduct8BitMixedSignednessAccelerated) && + visitor->Visit( + "integerDotProduct4x8BitPackedUnsignedAccelerated", + &properties->integerDotProduct4x8BitPackedUnsignedAccelerated) && + visitor->Visit( + "integerDotProduct4x8BitPackedSignedAccelerated", + &properties->integerDotProduct4x8BitPackedSignedAccelerated) && + visitor->Visit( + "integerDotProduct4x8BitPackedMixedSignednessAccelerated", + &properties + ->integerDotProduct4x8BitPackedMixedSignednessAccelerated) && + visitor->Visit("integerDotProduct16BitUnsignedAccelerated", + &properties->integerDotProduct16BitUnsignedAccelerated) && + visitor->Visit("integerDotProduct16BitSignedAccelerated", + &properties->integerDotProduct16BitSignedAccelerated) && + visitor->Visit( + "integerDotProduct16BitMixedSignednessAccelerated", + &properties->integerDotProduct16BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProduct32BitUnsignedAccelerated", + &properties->integerDotProduct32BitUnsignedAccelerated) && + visitor->Visit("integerDotProduct32BitSignedAccelerated", + &properties->integerDotProduct32BitSignedAccelerated) && + visitor->Visit( + "integerDotProduct32BitMixedSignednessAccelerated", + &properties->integerDotProduct32BitMixedSignednessAccelerated) && + visitor->Visit("integerDotProduct64BitUnsignedAccelerated", + &properties->integerDotProduct64BitUnsignedAccelerated) && + visitor->Visit("integerDotProduct64BitSignedAccelerated", + &properties->integerDotProduct64BitSignedAccelerated) && + visitor->Visit( + "integerDotProduct64BitMixedSignednessAccelerated", + &properties->integerDotProduct64BitMixedSignednessAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating8BitUnsignedAccelerated", + &properties + ->integerDotProductAccumulatingSaturating8BitUnsignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating8BitSignedAccelerated", + &properties + ->integerDotProductAccumulatingSaturating8BitSignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerate" + "d", + &properties + ->integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerat" + "ed", + &properties + ->integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerate" + "d", + &properties + ->integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAc" + "celerated", + &properties + ->integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating16BitUnsignedAccelerated", + &properties + ->integerDotProductAccumulatingSaturating16BitUnsignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating16BitSignedAccelerated", + &properties + ->integerDotProductAccumulatingSaturating16BitSignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerat" + "ed", + &properties + ->integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating32BitUnsignedAccelerated", + &properties + ->integerDotProductAccumulatingSaturating32BitUnsignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating32BitSignedAccelerated", + &properties + ->integerDotProductAccumulatingSaturating32BitSignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerat" + "ed", + &properties + ->integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating64BitUnsignedAccelerated", + &properties + ->integerDotProductAccumulatingSaturating64BitUnsignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating64BitSignedAccelerated", + &properties + ->integerDotProductAccumulatingSaturating64BitSignedAccelerated) && + visitor->Visit( + "integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerat" + "ed", + &properties + ->integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated) && + visitor->Visit("storageTexelBufferOffsetAlignmentBytes", + &properties->storageTexelBufferOffsetAlignmentBytes) && + visitor->Visit( + "storageTexelBufferOffsetSingleTexelAlignment", + &properties->storageTexelBufferOffsetSingleTexelAlignment) && + visitor->Visit("uniformTexelBufferOffsetAlignmentBytes", + &properties->uniformTexelBufferOffsetAlignmentBytes) && + visitor->Visit( + "uniformTexelBufferOffsetSingleTexelAlignment", + &properties->uniformTexelBufferOffsetSingleTexelAlignment) && + visitor->Visit("maxBufferSize", &properties->maxBufferSize); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkExtensionProperties* properties) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceVulkan13Features* features) { return - visitor->Visit("extensionName", &properties->extensionName) && - visitor->Visit("specVersion", &properties->specVersion); + + visitor->Visit("robustImageAccess", &features->robustImageAccess) && + visitor->Visit("inlineUniformBlock", &features->inlineUniformBlock) && + visitor->Visit( + "descriptorBindingInlineUniformBlockUpdateAfterBind", + &features->descriptorBindingInlineUniformBlockUpdateAfterBind) && + visitor->Visit("pipelineCreationCacheControl", + &features->pipelineCreationCacheControl) && + visitor->Visit("privateData", &features->privateData) && + visitor->Visit("shaderDemoteToHelperInvocation", + &features->shaderDemoteToHelperInvocation) && + visitor->Visit("shaderTerminateInvocation", + &features->shaderTerminateInvocation) && + visitor->Visit("subgroupSizeControl", &features->subgroupSizeControl) && + visitor->Visit("computeFullSubgroups", &features->computeFullSubgroups) && + visitor->Visit("synchronization2", &features->synchronization2) && + visitor->Visit("textureCompressionASTC_HDR", + &features->textureCompressionASTC_HDR) && + visitor->Visit("shaderZeroInitializeWorkgroupMemory", + &features->shaderZeroInitializeWorkgroupMemory) && + visitor->Visit("dynamicRendering", &features->dynamicRendering) && + visitor->Visit("shaderIntegerDotProduct", + &features->shaderIntegerDotProduct) && + visitor->Visit("maintenance4", &features->maintenance4); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkLayerProperties* properties) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceVulkan14Properties* properties) { return - visitor->Visit("layerName", &properties->layerName) && - visitor->Visit("specVersion", &properties->specVersion) && - visitor->Visit("implementationVersion", &properties->implementationVersion) && - visitor->Visit("description", &properties->description); + + visitor->Visit("lineSubPixelPrecisionBits", + &properties->lineSubPixelPrecisionBits) && + visitor->Visit("maxVertexAttribDivisor", + &properties->maxVertexAttribDivisor) && + visitor->Visit("supportsNonZeroFirstInstance", + &properties->supportsNonZeroFirstInstance) && + visitor->Visit("maxPushDescriptors", &properties->maxPushDescriptors) && + visitor->Visit( + "dynamicRenderingLocalReadDepthStencilAttachments", + &properties->dynamicRenderingLocalReadDepthStencilAttachments) && + visitor->Visit( + "dynamicRenderingLocalReadMultisampledAttachments", + &properties->dynamicRenderingLocalReadMultisampledAttachments) && + visitor->Visit( + "earlyFragmentMultisampleCoverageAfterSampleCounting", + &properties->earlyFragmentMultisampleCoverageAfterSampleCounting) && + visitor->Visit( + "earlyFragmentSampleMaskTestBeforeSampleCounting", + &properties->earlyFragmentSampleMaskTestBeforeSampleCounting) && + visitor->Visit("depthStencilSwizzleOneSupport", + &properties->depthStencilSwizzleOneSupport) && + visitor->Visit("polygonModePointSize", + &properties->polygonModePointSize) && + visitor->Visit( + "nonStrictSinglePixelWideLinesUseParallelogram", + &properties->nonStrictSinglePixelWideLinesUseParallelogram) && + visitor->Visit("nonStrictWideLinesUseParallelogram", + &properties->nonStrictWideLinesUseParallelogram) && + visitor->Visit("blockTexelViewCompatibleMultipleLayers", + &properties->blockTexelViewCompatibleMultipleLayers) && + visitor->Visit("maxCombinedImageSamplerDescriptorCount", + &properties->maxCombinedImageSamplerDescriptorCount) && + visitor->Visit("fragmentShadingRateClampCombinerInputs", + &properties->fragmentShadingRateClampCombinerInputs) && + visitor->Visit("defaultRobustnessStorageBuffers", + &properties->defaultRobustnessStorageBuffers) && + visitor->Visit("defaultRobustnessUniformBuffers", + &properties->defaultRobustnessUniformBuffers) && + visitor->Visit("defaultRobustnessVertexInputs", + &properties->defaultRobustnessVertexInputs) && + visitor->Visit("defaultRobustnessImages", + &properties->defaultRobustnessImages) && + visitor->Visit("copySrcLayoutCount", &properties->copySrcLayoutCount) && + visitor->VisitArray("pCopySrcLayouts", properties->copySrcLayoutCount, + &properties->pCopySrcLayouts) && + visitor->Visit("copyDstLayoutCount", &properties->copyDstLayoutCount) && + visitor->VisitArray("pCopyDstLayouts", properties->copyDstLayoutCount, + &properties->pCopyDstLayouts) && + visitor->Visit("optimalTilingLayoutUUID", + &properties->optimalTilingLayoutUUID) && + visitor->Visit("identicalMemoryTypeRequirements", + &properties->identicalMemoryTypeRequirements); } template <typename Visitor> -inline bool Iterate(Visitor* visitor, VkFormatProperties* properties) { +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceVulkan14Features* features) { return - visitor->Visit("linearTilingFeatures", &properties->linearTilingFeatures) && - visitor->Visit("optimalTilingFeatures", &properties->optimalTilingFeatures) && - visitor->Visit("bufferFeatures", &properties->bufferFeatures); + + visitor->Visit("globalPriorityQuery", &features->globalPriorityQuery) && + visitor->Visit("shaderSubgroupRotate", &features->shaderSubgroupRotate) && + visitor->Visit("shaderSubgroupRotateClustered", + &features->shaderSubgroupRotateClustered) && + visitor->Visit("shaderFloatControls2", &features->shaderFloatControls2) && + visitor->Visit("shaderExpectAssume", &features->shaderExpectAssume) && + visitor->Visit("rectangularLines", &features->rectangularLines) && + visitor->Visit("bresenhamLines", &features->bresenhamLines) && + visitor->Visit("smoothLines", &features->smoothLines) && + visitor->Visit("stippledRectangularLines", + &features->stippledRectangularLines) && + visitor->Visit("stippledBresenhamLines", + &features->stippledBresenhamLines) && + visitor->Visit("stippledSmoothLines", &features->stippledSmoothLines) && + visitor->Visit("vertexAttributeInstanceRateDivisor", + &features->vertexAttributeInstanceRateDivisor) && + visitor->Visit("vertexAttributeInstanceRateZeroDivisor", + &features->vertexAttributeInstanceRateZeroDivisor) && + visitor->Visit("indexTypeUint8", &features->indexTypeUint8) && + visitor->Visit("dynamicRenderingLocalRead", + &features->dynamicRenderingLocalRead) && + visitor->Visit("maintenance5", &features->maintenance5) && + visitor->Visit("maintenance6", &features->maintenance6) && + visitor->Visit("pipelineProtectedAccess", + &features->pipelineProtectedAccess) && + visitor->Visit("pipelineRobustness", &features->pipelineRobustness) && + visitor->Visit("hostImageCopy", &features->hostImageCopy); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkPhysicalDeviceDriverProperties* properties) { + return + + visitor->Visit("driverID", &properties->driverID) && + visitor->Visit("driverName", &properties->driverName) && + visitor->Visit("driverInfo", &properties->driverInfo) && + visitor->Visit("conformanceVersion", &properties->conformanceVersion); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, VkExternalFenceProperties* properties) { + return visitor->Visit("exportFromImportedHandleTypes", + &properties->exportFromImportedHandleTypes) && + visitor->Visit("compatibleHandleTypes", + &properties->compatibleHandleTypes) && + visitor->Visit("externalFenceFeatures", + &properties->externalFenceFeatures); +} + +template <typename Visitor> +inline bool Iterate(Visitor* visitor, + VkExternalSemaphoreProperties* properties) { + return visitor->Visit("exportFromImportedHandleTypes", + &properties->exportFromImportedHandleTypes) && + visitor->Visit("compatibleHandleTypes", + &properties->compatibleHandleTypes) && + visitor->Visit("externalSemaphoreFeatures", + &properties->externalSemaphoreFeatures); } template <typename Visitor> @@ -1197,6 +1953,7 @@ inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { ret &= visitor->Visit("core13", &device->core13); FALLTHROUGH_INTENDED; case VK_API_VERSION_1_2: + ret &= visitor->Visit("core11", &device->core11); ret &= visitor->Visit("core12", &device->core12); FALLTHROUGH_INTENDED; case VK_API_VERSION_1_1: @@ -1209,17 +1966,17 @@ inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { visitor->Visit("idProperties", &device->id_properties) && visitor->Visit("maintenance3Properties", &device->maintenance3_properties) && - visitor->Visit("16bitStorageFeatures", - &device->bit16_storage_features) && visitor->Visit("multiviewFeatures", &device->multiview_features) && - visitor->Visit("variablePointerFeatures", - &device->variable_pointer_features) && + visitor->Visit("variablePointersFeatures", + &device->variable_pointers_features) && visitor->Visit("protectedMemoryFeatures", &device->protected_memory_features) && visitor->Visit("samplerYcbcrConversionFeatures", &device->sampler_ycbcr_conversion_features) && visitor->Visit("shaderDrawParameterFeatures", &device->shader_draw_parameter_features) && + visitor->Visit("bit16StorageFeatures", + &device->bit16_storage_features) && visitor->Visit("externalFenceProperties", &device->external_fence_properties) && visitor->Visit("externalSemaphoreProperties", @@ -1233,17 +1990,90 @@ inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { visitor->Visit("extensions", &device->extensions) && visitor->Visit("layers", &device->layers) && visitor->Visit("formats", &device->formats); - if (device->ext_driver_properties.reported) { - ret &= visitor->Visit("VK_KHR_driver_properties", - &device->ext_driver_properties); - } - if (device->ext_variable_pointer_features.reported) { + + if (device->khr_variable_pointers.reported) { ret &= visitor->Visit("VK_KHR_variable_pointers", - &device->ext_variable_pointer_features); + &device->khr_variable_pointers); } - if (device->ext_shader_float16_int8_features.reported) { + if (device->khr_shader_float16_int8.reported) { ret &= visitor->Visit("VK_KHR_shader_float16_int8", - &device->ext_shader_float16_int8_features); + &device->khr_shader_float16_int8); + } + if (device->ext_image_2d_view_of_3d.reported) { + ret &= visitor->Visit("VK_EXT_image_2d_view_of_3d", + &device->ext_image_2d_view_of_3d); + } + if (device->ext_custom_border_color.reported) { + ret &= visitor->Visit("VK_EXT_custom_border_color", + &device->ext_custom_border_color); + } + if (device->ext_primitive_topology_list_restart.reported) { + ret &= visitor->Visit("VK_EXT_primitive_topology_list_restart", + &device->ext_primitive_topology_list_restart); + } + if (device->ext_provoking_vertex.reported) { + ret &= visitor->Visit("VK_EXT_provoking_vertex", + &device->ext_provoking_vertex); + } + if (device->khr_index_type_uint8.reported) { + ret &= visitor->Visit("VK_KHR_index_type_uint8", + &device->khr_index_type_uint8); + } + if (device->ext_index_type_uint8.reported) { + ret &= visitor->Visit("VK_EXT_index_type_uint8", + &device->ext_index_type_uint8); + } + if (device->khr_vertex_attribute_divisor.reported) { + ret &= visitor->Visit("VK_KHR_vertex_attribute_divisor", + &device->khr_vertex_attribute_divisor); + } + if (device->ext_vertex_attribute_divisor.reported) { + ret &= visitor->Visit("VK_EXT_vertex_attribute_divisor", + &device->ext_vertex_attribute_divisor); + } + if (device->ext_transform_feedback.reported) { + ret &= visitor->Visit("VK_EXT_transform_feedback", + &device->ext_transform_feedback); + } + if (device->khr_shader_subgroup_uniform_control_flow.reported) { + ret &= + visitor->Visit("VK_KHR_shader_subgroup_uniform_control_flow", + &device->khr_shader_subgroup_uniform_control_flow); + } + if (device->khr_shader_subgroup_extended_types.reported) { + ret &= visitor->Visit("VK_KHR_shader_subgroup_extended_types", + &device->khr_shader_subgroup_extended_types); + } + if (device->khr_8bit_storage.reported) { + ret &= visitor->Visit("VK_KHR_8bit_storage", &device->khr_8bit_storage); + } + if (device->khr_shader_integer_dot_product.reported) { + ret &= visitor->Visit("VK_KHR_shader_integer_dot_product", + &device->khr_shader_integer_dot_product); + } + if (device->img_relaxed_line_rasterization.reported) { + ret &= visitor->Visit("VK_IMG_relaxed_line_rasterization", + &device->img_relaxed_line_rasterization); + } + if (device->khr_line_rasterization.reported) { + ret &= visitor->Visit("VK_KHR_line_rasterization", + &device->khr_line_rasterization); + } + if (device->ext_line_rasterization.reported) { + ret &= visitor->Visit("VK_EXT_line_rasterization", + &device->ext_line_rasterization); + } + if (device->ext_primitives_generated_query.reported) { + ret &= visitor->Visit("VK_EXT_primitives_generated_query", + &device->ext_primitives_generated_query); + } + if (device->khr_shader_float_controls.reported) { + ret &= visitor->Visit("VK_KHR_shader_float_controls", + &device->khr_shader_float_controls); + } + if (device->khr_driver_properties.reported) { + ret &= visitor->Visit("VK_KHR_driver_properties", + &device->khr_driver_properties); } } return ret; @@ -1301,7 +2131,9 @@ inline Json::Value ToJsonValue(const uint64_t& value) { return Json::Value(string); } -template <typename T, typename = EnableForEnum<T>, typename = void, +template <typename T, + typename = EnableForEnum<T>, + typename = void, typename = void> inline Json::Value ToJsonValue(const T& value) { return Json::Value(static_cast<double>(value)); @@ -1310,7 +2142,8 @@ inline Json::Value ToJsonValue(const T& value) { template <typename T> inline Json::Value ArrayToJsonValue(uint32_t count, const T* values) { Json::Value array(Json::arrayValue); - for (unsigned int i = 0; i < count; ++i) array.append(ToJsonValue(values[i])); + for (unsigned int i = 0; i < count; ++i) + array.append(ToJsonValue(values[i])); return array; } @@ -1342,7 +2175,8 @@ inline Json::Value ToJsonValue(const std::pair<F, S>& value) { template <typename F, typename S> inline Json::Value ToJsonValue(const std::map<F, S>& value) { Json::Value array(Json::arrayValue); - for (auto& kv : value) array.append(ToJsonValue(kv)); + for (auto& kv : value) + array.append(ToJsonValue(kv)); return array; } @@ -1352,7 +2186,8 @@ class JsonWriterVisitor { ~JsonWriterVisitor() {} - template <typename T> bool Visit(const char* key, const T* value) { + template <typename T> + bool Visit(const char* key, const T* value) { object_[key] = ToJsonValue(*value); return true; } @@ -1365,7 +2200,7 @@ class JsonWriterVisitor { } template <typename T> - bool VisitArray(const char* key, uint32_t count, const T *value) { + bool VisitArray(const char* key, uint32_t count, const T* value) { object_[key] = ArrayToJsonValue(count, *value); return true; } @@ -1392,7 +2227,8 @@ template <typename T, typename = EnableForStruct<T>> bool AsValue(Json::Value* json_value, T* t); inline bool AsValue(Json::Value* json_value, int32_t* value) { - if (json_value->type() != Json::realValue) return false; + if (json_value->type() != Json::realValue) + return false; double d = json_value->asDouble(); if (!IsIntegral(d) || d < static_cast<double>(std::numeric_limits<int32_t>::min()) || @@ -1403,14 +2239,16 @@ inline bool AsValue(Json::Value* json_value, int32_t* value) { } inline bool AsValue(Json::Value* json_value, uint64_t* value) { - if (json_value->type() != Json::stringValue) return false; + if (json_value->type() != Json::stringValue) + return false; int result = std::sscanf(json_value->asString().c_str(), "0x%016" PRIx64, value); return result == 1; } inline bool AsValue(Json::Value* json_value, uint32_t* value) { - if (json_value->type() != Json::realValue) return false; + if (json_value->type() != Json::realValue) + return false; double d = json_value->asDouble(); if (!IsIntegral(d) || d < 0.0 || d > static_cast<double>(std::numeric_limits<uint32_t>::max())) @@ -1429,7 +2267,8 @@ inline bool AsValue(Json::Value* json_value, uint8_t* value) { } inline bool AsValue(Json::Value* json_value, float* value) { - if (json_value->type() != Json::realValue) return false; + if (json_value->type() != Json::realValue) + return false; *value = static_cast<float>(json_value->asDouble()); return true; } @@ -1438,7 +2277,8 @@ inline bool AsValue(Json::Value* json_value, VkImageLayout* t) { uint32_t value = 0; if (!AsValue(json_value, &value)) return false; - if (!EnumTraits<VkImageLayout>::exist(value)) return false; + if (!EnumTraits<VkImageLayout>::exist(value)) + return false; *t = static_cast<VkImageLayout>(value); return true; } @@ -1448,7 +2288,8 @@ inline bool AsArray(Json::Value* json_value, uint32_t count, T* values) { if (json_value->type() != Json::arrayValue || json_value->size() != count) return false; for (uint32_t i = 0; i < count; ++i) { - if (!AsValue(&(*json_value)[i], values + i)) return false; + if (!AsValue(&(*json_value)[i], values + i)) + return false; } return true; } @@ -1460,12 +2301,13 @@ inline bool AsValue(Json::Value* json_value, T (*value)[N]) { template <size_t N> inline bool AsValue(Json::Value* json_value, char (*value)[N]) { - if (json_value->type() != Json::stringValue) return false; + if (json_value->type() != Json::stringValue) + return false; size_t len = json_value->asString().length(); if (len >= N) return false; memcpy(*value, json_value->asString().c_str(), len); - memset(*value + len, 0, N-len); + memset(*value + len, 0, N - len); return true; } @@ -1473,15 +2315,17 @@ template <typename T, typename = EnableForEnum<T>, typename = void> inline bool AsValue(Json::Value* json_value, T* t) { uint32_t value = 0; if (!AsValue(json_value, &value)) - return false; - if (!EnumTraits<T>::exist(value)) return false; + return false; + if (!EnumTraits<T>::exist(value)) + return false; *t = static_cast<T>(value); return true; } template <typename T> inline bool AsValue(Json::Value* json_value, std::vector<T>* value) { - if (json_value->type() != Json::arrayValue) return false; + if (json_value->type() != Json::arrayValue) + return false; int size = json_value->size(); value->resize(size); return AsArray(json_value, size, value->data()); @@ -1497,11 +2341,13 @@ inline bool AsValue(Json::Value* json_value, std::pair<F, S>* value) { template <typename F, typename S> inline bool AsValue(Json::Value* json_value, std::map<F, S>* value) { - if (json_value->type() != Json::arrayValue) return false; + if (json_value->type() != Json::arrayValue) + return false; int size = json_value->size(); for (int i = 0; i < size; ++i) { std::pair<F, S> elem; - if (!AsValue(&(*json_value)[i], &elem)) return false; + if (!AsValue(&(*json_value)[i], &elem)) + return false; if (!value->insert(elem).second) return false; } @@ -1509,7 +2355,9 @@ inline bool AsValue(Json::Value* json_value, std::map<F, S>* value) { } template <typename T> -bool ReadValue(Json::Value* object, const char* key, T* value, +bool ReadValue(Json::Value* object, + const char* key, + T* value, std::string* errors) { Json::Value json_value = (*object)[key]; if (!json_value) { @@ -1517,7 +2365,8 @@ bool ReadValue(Json::Value* object, const char* key, T* value, *errors = std::string(key) + " missing."; return false; } - if (AsValue(&json_value, value)) return true; + if (AsValue(&json_value, value)) + return true; if (errors) *errors = std::string("Wrong type for ") + std::string(key) + "."; return false; @@ -1533,7 +2382,8 @@ class JsonReaderVisitor { JsonReaderVisitor(Json::Value* object, std::string* errors) : object_(object), errors_(errors) {} - template <typename T> bool Visit(const char* key, T* value) const { + template <typename T> + bool Visit(const char* key, T* value) const { return ReadValue(object_, key, value, errors_); } @@ -1547,27 +2397,28 @@ class JsonReaderVisitor { *errors_ = std::string(key) + " missing."; return false; } - if (AsArray(&json_value, count, *value)) return true; + if (AsArray(&json_value, count, *value)) + return true; if (errors_) *errors_ = std::string("Wrong type for ") + std::string(key) + "."; return false; } template <typename T> - bool VisitArray(const char* key, uint32_t count, T *value) { + bool VisitArray(const char* key, uint32_t count, T* value) { Json::Value json_value = (*object_)[key]; if (!json_value) { if (errors_) *errors_ = std::string(key) + " missing."; return false; } - if (AsArray(&json_value, count, *value)) return true; + if (AsArray(&json_value, count, *value)) + return true; if (errors_) *errors_ = std::string("Wrong type for ") + std::string(key) + "."; return false; } - private: Json::Value* object_; std::string* errors_; @@ -1575,21 +2426,21 @@ class JsonReaderVisitor { template <typename T, typename /*= EnableForStruct<T>*/> bool AsValue(Json::Value* json_value, T* t) { - if (json_value->type() != Json::objectValue) return false; + if (json_value->type() != Json::objectValue) + return false; JsonReaderVisitor visitor(json_value, nullptr); return VisitForRead(&visitor, t); } - -template <typename T> std::string VkTypeToJson(const T& t) { +template <typename T> +std::string VkTypeToJson(const T& t) { JsonWriterVisitor visitor; VisitForWrite(&visitor, t); return visitor.get_object().toStyledString(); } -template <typename T> bool VkTypeFromJson(const std::string& json, - T* t, - std::string* errors) { +template <typename T> +bool VkTypeFromJson(const std::string& json, T* t, std::string* errors) { *t = T(); Json::Value object(Json::objectValue); Json::CharReaderBuilder builder; diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h index 5818c73b16..d74644ca8e 100644 --- a/vulkan/vkjson/vkjson.h +++ b/vulkan/vkjson/vkjson.h @@ -21,8 +21,8 @@ #ifndef VKJSON_H_ #define VKJSON_H_ -#include <vulkan/vulkan.h> #include <string.h> +#include <vulkan/vulkan.h> #include <map> #include <string> @@ -38,34 +38,233 @@ struct VkJsonLayer { std::vector<VkExtensionProperties> extensions; }; -struct VkJsonExtDriverProperties { - VkJsonExtDriverProperties() { - reported = false; - memset(&driver_properties_khr, 0, - sizeof(VkPhysicalDeviceDriverPropertiesKHR)); - } - bool reported; - VkPhysicalDeviceDriverPropertiesKHR driver_properties_khr; -}; - -struct VkJsonExtVariablePointerFeatures { - VkJsonExtVariablePointerFeatures() { +struct VkJsonKHRVariablePointers { + VkJsonKHRVariablePointers() { reported = false; memset(&variable_pointer_features_khr, 0, sizeof(VkPhysicalDeviceVariablePointerFeaturesKHR)); + memset(&variable_pointers_features_khr, 0, + sizeof(VkPhysicalDeviceVariablePointersFeaturesKHR)); } bool reported; VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointer_features_khr; + VkPhysicalDeviceVariablePointersFeaturesKHR variable_pointers_features_khr; }; -struct VkJsonExtShaderFloat16Int8Features { - VkJsonExtShaderFloat16Int8Features() { +struct VkJsonKHRShaderFloat16Int8 { + VkJsonKHRShaderFloat16Int8() { reported = false; memset(&shader_float16_int8_features_khr, 0, sizeof(VkPhysicalDeviceShaderFloat16Int8FeaturesKHR)); + memset(&float16_int8_features_khr, 0, + sizeof(VkPhysicalDeviceFloat16Int8FeaturesKHR)); } bool reported; VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_float16_int8_features_khr; + VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_features_khr; +}; + +struct VkJsonExtImage2dViewOf3d { + VkJsonExtImage2dViewOf3d() { + reported = false; + memset(&image_2d_view_of_3d_features_ext, 0, + sizeof(VkPhysicalDeviceImage2DViewOf3DFeaturesEXT)); + } + bool reported; + VkPhysicalDeviceImage2DViewOf3DFeaturesEXT image_2d_view_of_3d_features_ext; +}; + +struct VkJsonExtCustomBorderColor { + VkJsonExtCustomBorderColor() { + reported = false; + memset(&custom_border_color_features_ext, 0, + sizeof(VkPhysicalDeviceCustomBorderColorFeaturesEXT)); + } + bool reported; + VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border_color_features_ext; +}; + +struct VkJsonExtPrimitiveTopologyListRestart { + VkJsonExtPrimitiveTopologyListRestart() { + reported = false; + memset(&primitive_topology_list_restart_features_ext, 0, + sizeof(VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT)); + } + bool reported; + VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT + primitive_topology_list_restart_features_ext; +}; + +struct VkJsonExtProvokingVertex { + VkJsonExtProvokingVertex() { + reported = false; + memset(&provoking_vertex_features_ext, 0, + sizeof(VkPhysicalDeviceProvokingVertexFeaturesEXT)); + } + bool reported; + VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex_features_ext; +}; + +struct VkJsonKHRIndexTypeUint8 { + VkJsonKHRIndexTypeUint8() { + reported = false; + memset(&index_type_uint8_features_khr, 0, + sizeof(VkPhysicalDeviceIndexTypeUint8FeaturesKHR)); + } + bool reported; + VkPhysicalDeviceIndexTypeUint8FeaturesKHR index_type_uint8_features_khr; +}; + +struct VkJsonExtIndexTypeUint8 { + VkJsonExtIndexTypeUint8() { + reported = false; + memset(&index_type_uint8_features_ext, 0, + sizeof(VkPhysicalDeviceIndexTypeUint8FeaturesEXT)); + } + bool reported; + VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8_features_ext; +}; + +struct VkJsonKHRVertexAttributeDivisor { + VkJsonKHRVertexAttributeDivisor() { + reported = false; + memset(&vertex_attribute_divisor_features_khr, 0, + sizeof(VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR)); + } + bool reported; + VkPhysicalDeviceVertexAttributeDivisorFeaturesKHR + vertex_attribute_divisor_features_khr; +}; + +struct VkJsonExtVertexAttributeDivisor { + VkJsonExtVertexAttributeDivisor() { + reported = false; + memset(&vertex_attribute_divisor_features_ext, 0, + sizeof(VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT)); + } + bool reported; + VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT + vertex_attribute_divisor_features_ext; +}; + +struct VkJsonExtTransformFeedback { + VkJsonExtTransformFeedback() { + reported = false; + memset(&transform_feedback_features_ext, 0, + sizeof(VkPhysicalDeviceTransformFeedbackFeaturesEXT)); + } + bool reported; + VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback_features_ext; +}; + +struct VkJsonKHRShaderSubgroupUniformControlFlow { + VkJsonKHRShaderSubgroupUniformControlFlow() { + reported = false; + memset(&shader_subgroup_uniform_control_flow_features_khr, 0, + sizeof(VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR)); + } + bool reported; + VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR + shader_subgroup_uniform_control_flow_features_khr; +}; + +struct VkJsonKHRShaderSubgroupExtendedTypes { + VkJsonKHRShaderSubgroupExtendedTypes() { + reported = false; + memset(&shader_subgroup_extended_types_features_khr, 0, + sizeof(VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR)); + } + bool reported; + VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR + shader_subgroup_extended_types_features_khr; +}; + +struct VkJsonKHR8bitStorage { + VkJsonKHR8bitStorage() { + reported = false; + memset(&bit8_storage_features_khr, 0, + sizeof(VkPhysicalDevice8BitStorageFeaturesKHR)); + } + bool reported; + VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage_features_khr; +}; + +struct VkJsonKHRShaderIntegerDotProduct { + VkJsonKHRShaderIntegerDotProduct() { + reported = false; + memset(&shader_integer_dot_product_features_khr, 0, + sizeof(VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR)); + } + bool reported; + VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR + shader_integer_dot_product_features_khr; +}; + +struct VkJsonIMGRelaxedLineRasterization { + VkJsonIMGRelaxedLineRasterization() { + reported = false; + memset(&relaxed_line_rasterization_features_img, 0, + sizeof(VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG)); + } + bool reported; + VkPhysicalDeviceRelaxedLineRasterizationFeaturesIMG + relaxed_line_rasterization_features_img; +}; + +struct VkJsonKHRLineRasterization { + VkJsonKHRLineRasterization() { + reported = false; + memset(&line_rasterization_features_khr, 0, + sizeof(VkPhysicalDeviceLineRasterizationFeaturesKHR)); + } + bool reported; + VkPhysicalDeviceLineRasterizationFeaturesKHR line_rasterization_features_khr; +}; + +struct VkJsonExtLineRasterization { + VkJsonExtLineRasterization() { + reported = false; + memset(&line_rasterization_features_ext, 0, + sizeof(VkPhysicalDeviceLineRasterizationFeaturesEXT)); + } + bool reported; + VkPhysicalDeviceLineRasterizationFeaturesEXT line_rasterization_features_ext; +}; + +struct VkJsonExtPrimitivesGeneratedQuery { + VkJsonExtPrimitivesGeneratedQuery() { + reported = false; + memset(&primitives_generated_query_features_ext, 0, + sizeof(VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT)); + } + bool reported; + VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT + primitives_generated_query_features_ext; +}; + +struct VkJsonKHRShaderFloatControls { + VkJsonKHRShaderFloatControls() { + reported = false; + memset(&float_controls_properties_khr, 0, + sizeof(VkPhysicalDeviceFloatControlsPropertiesKHR)); + } + bool reported; + VkPhysicalDeviceFloatControlsPropertiesKHR float_controls_properties_khr; +}; + +struct VkJsonKHRDriverProperties { + VkJsonKHRDriverProperties() { + reported = false; + memset(&driver_properties_khr, 0, + sizeof(VkPhysicalDeviceDriverPropertiesKHR)); + } + bool reported; + VkPhysicalDeviceDriverPropertiesKHR driver_properties_khr; +}; + +struct VkJsonCore11 { + VkPhysicalDeviceVulkan11Properties properties; + VkPhysicalDeviceVulkan11Features features; }; struct VkJsonCore12 { @@ -99,28 +298,48 @@ struct VkJsonDevice { memset(&bit16_storage_features, 0, sizeof(VkPhysicalDevice16BitStorageFeatures)); memset(&multiview_features, 0, sizeof(VkPhysicalDeviceMultiviewFeatures)); - memset(&variable_pointer_features, 0, - sizeof(VkPhysicalDeviceVariablePointerFeatures)); + memset(&variable_pointers_features, 0, + sizeof(VkPhysicalDeviceVariablePointersFeatures)); memset(&protected_memory_features, 0, sizeof(VkPhysicalDeviceProtectedMemoryFeatures)); memset(&sampler_ycbcr_conversion_features, 0, sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures)); memset(&shader_draw_parameter_features, 0, sizeof(VkPhysicalDeviceShaderDrawParameterFeatures)); + memset(&core11, 0, sizeof(VkJsonCore11)); memset(&core12, 0, sizeof(VkJsonCore12)); memset(&core13, 0, sizeof(VkJsonCore13)); memset(&core14, 0, sizeof(VkJsonCore14)); } + VkJsonKHRVariablePointers khr_variable_pointers; + VkJsonKHRShaderFloat16Int8 khr_shader_float16_int8; + VkJsonExtImage2dViewOf3d ext_image_2d_view_of_3d; + VkJsonExtCustomBorderColor ext_custom_border_color; + VkJsonExtPrimitiveTopologyListRestart ext_primitive_topology_list_restart; + VkJsonExtProvokingVertex ext_provoking_vertex; + VkJsonKHRIndexTypeUint8 khr_index_type_uint8; + VkJsonExtIndexTypeUint8 ext_index_type_uint8; + VkJsonKHRVertexAttributeDivisor khr_vertex_attribute_divisor; + VkJsonExtVertexAttributeDivisor ext_vertex_attribute_divisor; + VkJsonExtTransformFeedback ext_transform_feedback; + VkJsonKHRShaderSubgroupUniformControlFlow + khr_shader_subgroup_uniform_control_flow; + VkJsonKHRShaderSubgroupExtendedTypes khr_shader_subgroup_extended_types; + VkJsonKHR8bitStorage khr_8bit_storage; + VkJsonKHRShaderIntegerDotProduct khr_shader_integer_dot_product; + VkJsonIMGRelaxedLineRasterization img_relaxed_line_rasterization; + VkJsonKHRLineRasterization khr_line_rasterization; + VkJsonExtLineRasterization ext_line_rasterization; + VkJsonExtPrimitivesGeneratedQuery ext_primitives_generated_query; + VkJsonKHRShaderFloatControls khr_shader_float_controls; + VkJsonKHRDriverProperties khr_driver_properties; + VkJsonCore11 core11; + VkJsonCore12 core12; + VkJsonCore13 core13; + VkJsonCore14 core14; VkPhysicalDeviceProperties properties; VkPhysicalDeviceFeatures features; - VkJsonExtDriverProperties ext_driver_properties; - VkJsonExtVariablePointerFeatures ext_variable_pointer_features; - VkJsonExtShaderFloat16Int8Features ext_shader_float16_int8_features; VkPhysicalDeviceMemoryProperties memory; - std::vector<VkQueueFamilyProperties> queues; - std::vector<VkExtensionProperties> extensions; - std::vector<VkLayerProperties> layers; - std::map<VkFormat, VkFormatProperties> formats; VkPhysicalDeviceSubgroupProperties subgroup_properties; VkPhysicalDevicePointClippingProperties point_clipping_properties; VkPhysicalDeviceMultiviewProperties multiview_properties; @@ -128,18 +347,19 @@ struct VkJsonDevice { VkPhysicalDeviceMaintenance3Properties maintenance3_properties; VkPhysicalDevice16BitStorageFeatures bit16_storage_features; VkPhysicalDeviceMultiviewFeatures multiview_features; - VkPhysicalDeviceVariablePointerFeatures variable_pointer_features; + VkPhysicalDeviceVariablePointersFeatures variable_pointers_features; VkPhysicalDeviceProtectedMemoryFeatures protected_memory_features; VkPhysicalDeviceSamplerYcbcrConversionFeatures sampler_ycbcr_conversion_features; VkPhysicalDeviceShaderDrawParameterFeatures shader_draw_parameter_features; + std::vector<VkQueueFamilyProperties> queues; + std::vector<VkExtensionProperties> extensions; + std::vector<VkLayerProperties> layers; + std::map<VkFormat, VkFormatProperties> formats; std::map<VkExternalFenceHandleTypeFlagBits, VkExternalFenceProperties> external_fence_properties; std::map<VkExternalSemaphoreHandleTypeFlagBits, VkExternalSemaphoreProperties> external_semaphore_properties; - VkJsonCore12 core12; - VkJsonCore13 core13; - VkJsonCore14 core14; }; struct VkJsonDeviceGroup { @@ -193,4 +413,4 @@ inline bool VkJsonAllPropertiesFromJson(const std::string& json, return VkJsonDeviceFromJson(json, properties, errors); } -#endif // VKJSON_H_ +#endif // VKJSON_H_
\ No newline at end of file diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc index 32bc50b5f8..22b3204e61 100644 --- a/vulkan/vkjson/vkjson_instance.cc +++ b/vulkan/vkjson/vkjson_instance.cc @@ -79,13 +79,25 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { nullptr, {}, }; + + if (HasExtension("VK_KHR_shader_float_controls", device.extensions)) { + device.khr_shader_float_controls.reported = true; + device.khr_shader_float_controls.float_controls_properties_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES; + device.khr_shader_float_controls.float_controls_properties_khr.pNext = + properties.pNext; + properties.pNext = + &device.khr_shader_float_controls.float_controls_properties_khr; + } + if (HasExtension("VK_KHR_driver_properties", device.extensions)) { - device.ext_driver_properties.reported = true; - device.ext_driver_properties.driver_properties_khr.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; - device.ext_driver_properties.driver_properties_khr.pNext = properties.pNext; - properties.pNext = &device.ext_driver_properties.driver_properties_khr; + device.khr_driver_properties.reported = true; + device.khr_driver_properties.driver_properties_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + device.khr_driver_properties.driver_properties_khr.pNext = properties.pNext; + properties.pNext = &device.khr_driver_properties.driver_properties_khr; } + vkGetPhysicalDeviceProperties2(physical_device, &properties); device.properties = properties.properties; @@ -94,25 +106,215 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { nullptr, {}, }; + if (HasExtension("VK_KHR_variable_pointers", device.extensions)) { - device.ext_variable_pointer_features.reported = true; - device.ext_variable_pointer_features.variable_pointer_features_khr.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR; - device.ext_variable_pointer_features.variable_pointer_features_khr.pNext = + device.khr_variable_pointers.reported = true; + device.khr_variable_pointers.variable_pointer_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES; + device.khr_variable_pointers.variable_pointer_features_khr.pNext = features.pNext; features.pNext = - &device.ext_variable_pointer_features.variable_pointer_features_khr; + &device.khr_variable_pointers.variable_pointer_features_khr; + device.khr_variable_pointers.variable_pointers_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES; + device.khr_variable_pointers.variable_pointers_features_khr.pNext = + features.pNext; + features.pNext = + &device.khr_variable_pointers.variable_pointers_features_khr; } + if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) { - device.ext_shader_float16_int8_features.reported = true; - device.ext_shader_float16_int8_features.shader_float16_int8_features_khr + device.khr_shader_float16_int8.reported = true; + device.khr_shader_float16_int8.shader_float16_int8_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES; + device.khr_shader_float16_int8.shader_float16_int8_features_khr.pNext = + features.pNext; + features.pNext = + &device.khr_shader_float16_int8.shader_float16_int8_features_khr; + device.khr_shader_float16_int8.float16_int8_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES; + device.khr_shader_float16_int8.float16_int8_features_khr.pNext = + features.pNext; + features.pNext = &device.khr_shader_float16_int8.float16_int8_features_khr; + } + + if (HasExtension("VK_EXT_image_2d_view_of_3d", device.extensions)) { + device.ext_image_2d_view_of_3d.reported = true; + device.ext_image_2d_view_of_3d.image_2d_view_of_3d_features_ext.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT; + device.ext_image_2d_view_of_3d.image_2d_view_of_3d_features_ext.pNext = + features.pNext; + features.pNext = + &device.ext_image_2d_view_of_3d.image_2d_view_of_3d_features_ext; + } + + if (HasExtension("VK_EXT_custom_border_color", device.extensions)) { + device.ext_custom_border_color.reported = true; + device.ext_custom_border_color.custom_border_color_features_ext.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT; + device.ext_custom_border_color.custom_border_color_features_ext.pNext = + features.pNext; + features.pNext = + &device.ext_custom_border_color.custom_border_color_features_ext; + } + + if (HasExtension("VK_EXT_primitive_topology_list_restart", + device.extensions)) { + device.ext_primitive_topology_list_restart.reported = true; + device.ext_primitive_topology_list_restart + .primitive_topology_list_restart_features_ext.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT; + device.ext_primitive_topology_list_restart + .primitive_topology_list_restart_features_ext.pNext = features.pNext; + features.pNext = &device.ext_primitive_topology_list_restart + .primitive_topology_list_restart_features_ext; + } + + if (HasExtension("VK_EXT_provoking_vertex", device.extensions)) { + device.ext_provoking_vertex.reported = true; + device.ext_provoking_vertex.provoking_vertex_features_ext.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT; + device.ext_provoking_vertex.provoking_vertex_features_ext.pNext = + features.pNext; + features.pNext = &device.ext_provoking_vertex.provoking_vertex_features_ext; + } + + if (HasExtension("VK_KHR_index_type_uint8", device.extensions)) { + device.khr_index_type_uint8.reported = true; + device.khr_index_type_uint8.index_type_uint8_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES; + device.khr_index_type_uint8.index_type_uint8_features_khr.pNext = + features.pNext; + features.pNext = &device.khr_index_type_uint8.index_type_uint8_features_khr; + } + + if (HasExtension("VK_EXT_index_type_uint8", device.extensions)) { + device.ext_index_type_uint8.reported = true; + device.ext_index_type_uint8.index_type_uint8_features_ext.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES; + device.ext_index_type_uint8.index_type_uint8_features_ext.pNext = + features.pNext; + features.pNext = &device.ext_index_type_uint8.index_type_uint8_features_ext; + } + + if (HasExtension("VK_KHR_vertex_attribute_divisor", device.extensions)) { + device.khr_vertex_attribute_divisor.reported = true; + device.khr_vertex_attribute_divisor.vertex_attribute_divisor_features_khr .sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR; - device.ext_shader_float16_int8_features.shader_float16_int8_features_khr + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES; + device.khr_vertex_attribute_divisor.vertex_attribute_divisor_features_khr .pNext = features.pNext; - features.pNext = &device.ext_shader_float16_int8_features - .shader_float16_int8_features_khr; + features.pNext = &device.khr_vertex_attribute_divisor + .vertex_attribute_divisor_features_khr; } + + if (HasExtension("VK_EXT_vertex_attribute_divisor", device.extensions)) { + device.ext_vertex_attribute_divisor.reported = true; + device.ext_vertex_attribute_divisor.vertex_attribute_divisor_features_ext + .sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES; + device.ext_vertex_attribute_divisor.vertex_attribute_divisor_features_ext + .pNext = features.pNext; + features.pNext = &device.ext_vertex_attribute_divisor + .vertex_attribute_divisor_features_ext; + } + + if (HasExtension("VK_EXT_transform_feedback", device.extensions)) { + device.ext_transform_feedback.reported = true; + device.ext_transform_feedback.transform_feedback_features_ext.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT; + device.ext_transform_feedback.transform_feedback_features_ext.pNext = + features.pNext; + features.pNext = + &device.ext_transform_feedback.transform_feedback_features_ext; + } + + if (HasExtension("VK_KHR_shader_subgroup_uniform_control_flow", + device.extensions)) { + device.khr_shader_subgroup_uniform_control_flow.reported = true; + device.khr_shader_subgroup_uniform_control_flow + .shader_subgroup_uniform_control_flow_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR; + device.khr_shader_subgroup_uniform_control_flow + .shader_subgroup_uniform_control_flow_features_khr.pNext = + features.pNext; + features.pNext = &device.khr_shader_subgroup_uniform_control_flow + .shader_subgroup_uniform_control_flow_features_khr; + } + + if (HasExtension("VK_KHR_shader_subgroup_extended_types", + device.extensions)) { + device.khr_shader_subgroup_extended_types.reported = true; + device.khr_shader_subgroup_extended_types + .shader_subgroup_extended_types_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES; + device.khr_shader_subgroup_extended_types + .shader_subgroup_extended_types_features_khr.pNext = features.pNext; + features.pNext = &device.khr_shader_subgroup_extended_types + .shader_subgroup_extended_types_features_khr; + } + + if (HasExtension("VK_KHR_8bit_storage", device.extensions)) { + device.khr_8bit_storage.reported = true; + device.khr_8bit_storage.bit8_storage_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES; + device.khr_8bit_storage.bit8_storage_features_khr.pNext = features.pNext; + features.pNext = &device.khr_8bit_storage.bit8_storage_features_khr; + } + + if (HasExtension("VK_KHR_shader_integer_dot_product", device.extensions)) { + device.khr_shader_integer_dot_product.reported = true; + device.khr_shader_integer_dot_product + .shader_integer_dot_product_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES; + device.khr_shader_integer_dot_product + .shader_integer_dot_product_features_khr.pNext = features.pNext; + features.pNext = &device.khr_shader_integer_dot_product + .shader_integer_dot_product_features_khr; + } + + if (HasExtension("VK_IMG_relaxed_line_rasterization", device.extensions)) { + device.img_relaxed_line_rasterization.reported = true; + device.img_relaxed_line_rasterization + .relaxed_line_rasterization_features_img.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RELAXED_LINE_RASTERIZATION_FEATURES_IMG; + device.img_relaxed_line_rasterization + .relaxed_line_rasterization_features_img.pNext = features.pNext; + features.pNext = &device.img_relaxed_line_rasterization + .relaxed_line_rasterization_features_img; + } + + if (HasExtension("VK_KHR_line_rasterization", device.extensions)) { + device.khr_line_rasterization.reported = true; + device.khr_line_rasterization.line_rasterization_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES; + device.khr_line_rasterization.line_rasterization_features_khr.pNext = + features.pNext; + features.pNext = + &device.khr_line_rasterization.line_rasterization_features_khr; + } + + if (HasExtension("VK_EXT_line_rasterization", device.extensions)) { + device.ext_line_rasterization.reported = true; + device.ext_line_rasterization.line_rasterization_features_ext.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES; + device.ext_line_rasterization.line_rasterization_features_ext.pNext = + features.pNext; + features.pNext = + &device.ext_line_rasterization.line_rasterization_features_ext; + } + + if (HasExtension("VK_EXT_primitives_generated_query", device.extensions)) { + device.ext_primitives_generated_query.reported = true; + device.ext_primitives_generated_query + .primitives_generated_query_features_ext.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT; + device.ext_primitives_generated_query + .primitives_generated_query_features_ext.pNext = features.pNext; + features.pNext = &device.ext_primitives_generated_query + .primitives_generated_query_features_ext; + } + vkGetPhysicalDeviceFeatures2(physical_device, &features); device.features = features.features; @@ -184,20 +386,15 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { vkGetPhysicalDeviceProperties2(physical_device, &properties); - device.bit16_storage_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; - device.bit16_storage_features.pNext = features.pNext; - features.pNext = &device.bit16_storage_features; - device.multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; device.multiview_features.pNext = features.pNext; features.pNext = &device.multiview_features; - device.variable_pointer_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES; - device.variable_pointer_features.pNext = features.pNext; - features.pNext = &device.variable_pointer_features; + device.variable_pointers_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES; + device.variable_pointers_features.pNext = features.pNext; + features.pNext = &device.variable_pointers_features; device.protected_memory_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; @@ -210,10 +407,15 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { features.pNext = &device.sampler_ycbcr_conversion_features; device.shader_draw_parameter_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES; + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES; device.shader_draw_parameter_features.pNext = features.pNext; features.pNext = &device.shader_draw_parameter_features; + device.bit16_storage_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; + device.bit16_storage_features.pNext = features.pNext; + features.pNext = &device.bit16_storage_features; + vkGetPhysicalDeviceFeatures2(physical_device, &features); VkPhysicalDeviceExternalFenceInfo external_fence_info = { @@ -261,6 +463,11 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { } if (device.properties.apiVersion >= VK_API_VERSION_1_2) { + device.core11.properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; + device.core11.properties.pNext = properties.pNext; + properties.pNext = &device.core11.properties; + device.core12.properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES; device.core12.properties.pNext = properties.pNext; @@ -268,6 +475,11 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { vkGetPhysicalDeviceProperties2(physical_device, &properties); + device.core11.features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + device.core11.features.pNext = features.pNext; + features.pNext = &device.core11.features; + device.core12.features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; device.core12.features.pNext = features.pNext; @@ -327,7 +539,8 @@ VkJsonInstance VkJsonGetInstance() { return VkJsonInstance(); instance.layers.reserve(count); for (auto& layer : layers) { - instance.layers.push_back(VkJsonLayer{layer, std::vector<VkExtensionProperties>()}); + instance.layers.push_back( + VkJsonLayer{layer, std::vector<VkExtensionProperties>()}); if (!EnumerateExtensions(layer.layerName, &instance.layers.back().extensions)) return VkJsonInstance(); |