diff options
author | Xin Li <delphij@google.com> | 2017-11-14 14:24:07 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2017-11-14 14:24:07 -0800 |
commit | 4994440faf5924da9987bb8f30b1193f3d8090be (patch) | |
tree | 5e609b3f0ab4d831641f82896f532d97ddc9dc1a | |
parent | c8ec93afd36ced43e0702f87f8ac1a5e4de36cf1 (diff) | |
parent | 2fc42a279697a847d5b6961914f6f6afbed49a48 (diff) | |
download | native-4994440faf5924da9987bb8f30b1193f3d8090be.tar.gz |
Merge commit '2fc42a279697a847d5b6961914f6f6afbed49a48' from
oc-mr1-dev-plus-aosp into stage-aosp-master.
Change-Id: I754fe8c1ec11f047e58694a2fdc8d9ab1b85cab5
262 files changed, 9619 insertions, 3028 deletions
diff --git a/Android.bp b/Android.bp index cd05b21dde..de9ea86f1d 100644 --- a/Android.bp +++ b/Android.bp @@ -8,8 +8,15 @@ ndk_headers { subdirs = [ "cmds/*", + "headers", "libs/*", "opengl", "services/*", "vulkan", ] + +cc_library_headers { + name: "libandroid_sensor_headers", + vendor: true, + export_include_dirs: ["include_sensor"], +} diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp index c6aab4c55c..b850390d93 100644 --- a/cmds/atrace/Android.bp +++ b/cmds/atrace/Android.bp @@ -15,6 +15,7 @@ cc_binary { "libhidltransport", "liblog", "libutils", + "libcutils", "libz", "libbase", ], diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index a478d4c123..9dbbb7757c 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -124,6 +124,7 @@ static const TracingCategory k_categories[] = { { OPT, "events/sched/sched_waking/enable" }, { OPT, "events/sched/sched_blocked_reason/enable" }, { OPT, "events/sched/sched_cpu_hotplug/enable" }, + { OPT, "events/cgroup/enable" }, } }, { "irq", "IRQ Events", 0, { { REQ, "events/irq/enable" }, diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 526fd19e30..3ea1d56771 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -25,6 +25,8 @@ on post-fs chown root shell /sys/kernel/tracing/events/sched/sched_blocked_reason/enable chown root shell /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable chown root shell /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable + chown root shell /sys/kernel/debug/tracing/events/cgroup/enable + chown root shell /sys/kernel/tracing/events/cgroup/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chown root shell /sys/kernel/tracing/events/power/cpu_frequency/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable @@ -77,6 +79,8 @@ on post-fs chmod 0664 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable chmod 0664 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable + chmod 0664 /sys/kernel/debug/tracing/events/cgroup/enable + chmod 0664 /sys/kernel/tracing/events/cgroup/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chmod 0664 /sys/kernel/tracing/events/power/cpu_frequency/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 5984093ed3..f908cbfe28 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -42,6 +42,9 @@ cc_library_shared { name: "libdumpstateutil", defaults: ["dumpstate_defaults"], vendor_available: true, + vndk: { + enabled: true, + }, header_libs: ["dumpstate_headers"], export_header_lib_headers: ["dumpstate_headers"], srcs: [ diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index a96033309c..ea5fbf1ae2 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -17,31 +17,6 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_SRC_FILES := \ tests/dumpstate_test_fixture.cpp -LOCAL_MODULE_CLASS := NATIVE_TESTS - -dumpstate_tests_intermediates := $(local-intermediates-dir)/DATA -dumpstate_tests_subpath_from_data := nativetest/dumpstate_test_fixture -dumpstate_tests_root_in_device := /data/$(dumpstate_tests_subpath_from_data) -dumpstate_tests_root_for_test_zip := $(dumpstate_tests_intermediates)/$(dumpstate_tests_subpath_from_data) -testdata_files := $(call find-subdir-files, testdata/*) - -# Copy test data files to intermediates/DATA for use with LOCAL_PICKUP_FILES -GEN := $(addprefix $(dumpstate_tests_root_for_test_zip)/, $(testdata_files)) -$(GEN): PRIVATE_PATH := $(LOCAL_PATH) -$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@ -$(GEN): $(dumpstate_tests_root_for_test_zip)/testdata/% : $(LOCAL_PATH)/testdata/% - $(transform-generated-source) -LOCAL_GENERATED_SOURCES += $(GEN) - -# Copy test data files again to $OUT/data so the tests can be run with adb sync -# TODO: the build system should do this automatically -GEN := $(addprefix $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/, $(testdata_files)) -$(GEN): PRIVATE_PATH := $(LOCAL_PATH) -$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@ -$(GEN): $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/testdata/% : $(LOCAL_PATH)/testdata/% - $(transform-generated-source) -LOCAL_GENERATED_SOURCES += $(GEN) - -LOCAL_PICKUP_FILES := $(dumpstate_tests_intermediates) +LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, tests/testdata) include $(BUILD_NATIVE_TEST) diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp index 7076791705..83e30a22ff 100644 --- a/cmds/dumpstate/DumpstateInternal.cpp +++ b/cmds/dumpstate/DumpstateInternal.cpp @@ -19,6 +19,8 @@ #include "DumpstateInternal.h" #include <errno.h> +#include <grp.h> +#include <pwd.h> #include <stdint.h> #include <stdio.h> #include <string.h> @@ -34,7 +36,6 @@ #include <android-base/file.h> #include <log/log.h> -#include <private/android_filesystem_config.h> uint64_t Nanotime() { timespec ts; @@ -44,7 +45,17 @@ uint64_t Nanotime() { // Switches to non-root user and group. bool DropRootUser() { - if (getgid() == AID_SHELL && getuid() == AID_SHELL) { + struct group* grp = getgrnam("shell"); + gid_t shell_gid = grp != nullptr ? grp->gr_gid : 0; + struct passwd* pwd = getpwnam("shell"); + uid_t shell_uid = pwd != nullptr ? pwd->pw_uid : 0; + + if (!shell_gid || !shell_uid) { + MYLOGE("Unable to get AID_SHELL: %s\n", strerror(errno)); + return false; + } + + if (getgid() == shell_gid && getuid() == shell_uid) { MYLOGD("drop_root_user(): already running as Shell\n"); return true; } @@ -54,17 +65,28 @@ bool DropRootUser() { return false; } - gid_t groups[] = {AID_LOG, AID_SDCARD_R, AID_SDCARD_RW, AID_MOUNT, - AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_BLUETOOTH}; - if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) { + static const std::vector<std::string> group_names{ + "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", "readproc", "bluetooth"}; + std::vector<gid_t> groups(group_names.size(), 0); + for (size_t i = 0; i < group_names.size(); ++i) { + grp = getgrnam(group_names[i].c_str()); + groups[i] = grp != nullptr ? grp->gr_gid : 0; + if (groups[i] == 0) { + MYLOGE("Unable to get required gid '%s': %s\n", group_names[i].c_str(), + strerror(errno)); + return false; + } + } + + if (setgroups(groups.size(), groups.data()) != 0) { MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno)); return false; } - if (setgid(AID_SHELL) != 0) { + if (setgid(shell_gid) != 0) { MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno)); return false; } - if (setuid(AID_SHELL) != 0) { + if (setuid(shell_uid) != 0) { MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno)); return false; } diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 9a3030e960..e792942670 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -126,8 +126,7 @@ static const std::string ZIP_ROOT_DIR = "FS"; static const std::string kDumpstateBoardPath = "/bugreports/"; static const std::string kDumpstateBoardFiles[] = { "dumpstate_board.txt", - // TODO: rename to dumpstate_board.bin once vendors can handle it - "modem_log_all.tar" + "dumpstate_board.bin" }; static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles); @@ -145,10 +144,12 @@ static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot( * Returns a vector of dump fds under |dir_path| with a given |file_prefix|. * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime| * is set, the vector only contains files that were written in the last 30 minutes. + * If |limit_by_count| is set, the vector only contains the ten latest files. */ static std::vector<DumpData>* GetDumpFds(const std::string& dir_path, const std::string& file_prefix, - bool limit_by_mtime) { + bool limit_by_mtime, + bool limit_by_count = true) { const time_t thirty_minutes_ago = ds.now_ - 60 * 30; std::unique_ptr<std::vector<DumpData>> dump_data(new std::vector<DumpData>()); @@ -191,6 +192,10 @@ static std::vector<DumpData>* GetDumpFds(const std::string& dir_path, std::sort(dump_data->begin(), dump_data->end()); + if (limit_by_count && dump_data->size() > 10) { + dump_data->erase(dump_data->begin() + 10, dump_data->end()); + } + return dump_data.release(); } diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 52698b28e8..92b0c0d8bc 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -94,7 +94,7 @@ class DumpstateBaseTest : public Test { protected: const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str()); const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/"; - const std::string kTestDataPath = kFixturesPath + "/testdata/"; + const std::string kTestDataPath = kFixturesPath + "tests/testdata/"; const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture"; const std::string kEchoCommand = "/system/bin/echo"; diff --git a/cmds/dumpstate/testdata/empty-file.txt b/cmds/dumpstate/tests/testdata/empty-file.txt index e69de29bb2..e69de29bb2 100644 --- a/cmds/dumpstate/testdata/empty-file.txt +++ b/cmds/dumpstate/tests/testdata/empty-file.txt diff --git a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt b/cmds/dumpstate/tests/testdata/multiple-lines-with-newline.txt index 7b7a187ff4..7b7a187ff4 100644 --- a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt +++ b/cmds/dumpstate/tests/testdata/multiple-lines-with-newline.txt diff --git a/cmds/dumpstate/testdata/multiple-lines.txt b/cmds/dumpstate/tests/testdata/multiple-lines.txt index bead1030b9..bead1030b9 100644 --- a/cmds/dumpstate/testdata/multiple-lines.txt +++ b/cmds/dumpstate/tests/testdata/multiple-lines.txt diff --git a/cmds/dumpstate/testdata/single-line-with-newline.txt b/cmds/dumpstate/tests/testdata/single-line-with-newline.txt index cb48c8263d..cb48c8263d 100644 --- a/cmds/dumpstate/testdata/single-line-with-newline.txt +++ b/cmds/dumpstate/tests/testdata/single-line-with-newline.txt diff --git a/cmds/dumpstate/testdata/single-line.txt b/cmds/dumpstate/tests/testdata/single-line.txt index 2f64046c45..2f64046c45 100644 --- a/cmds/dumpstate/testdata/single-line.txt +++ b/cmds/dumpstate/tests/testdata/single-line.txt diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-NAN.txt index dad9fe8182..dad9fe8182 100644 --- a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt +++ b/cmds/dumpstate/tests/testdata/stats-invalid-1st-NAN.txt diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-negative.txt index 4facef9518..4facef9518 100644 --- a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt +++ b/cmds/dumpstate/tests/testdata/stats-invalid-1st-negative.txt diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-too-big.txt index 42508f143c..42508f143c 100644 --- a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt +++ b/cmds/dumpstate/tests/testdata/stats-invalid-1st-too-big.txt diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-NAN.txt index a23ba2c76a..a23ba2c76a 100644 --- a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt +++ b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-NAN.txt diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-negative.txt index dd529b4d50..dd529b4d50 100644 --- a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt +++ b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-negative.txt diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-too-big.txt index b148b46614..b148b46614 100644 --- a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt +++ b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-too-big.txt diff --git a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-both-NAN.txt index 4a9466d5d7..4a9466d5d7 100644 --- a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt +++ b/cmds/dumpstate/tests/testdata/stats-invalid-both-NAN.txt diff --git a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt b/cmds/dumpstate/tests/testdata/stats-one-run-no-newline.txt index 0aef60ca08..0aef60ca08 100644 --- a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt +++ b/cmds/dumpstate/tests/testdata/stats-one-run-no-newline.txt diff --git a/cmds/dumpstate/testdata/stats-two-runs.txt b/cmds/dumpstate/tests/testdata/stats-two-runs.txt index 9af123310d..9af123310d 100644 --- a/cmds/dumpstate/testdata/stats-two-runs.txt +++ b/cmds/dumpstate/tests/testdata/stats-two-runs.txt diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 73c7f18236..32277499a6 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -16,6 +16,7 @@ #include <algorithm> #include <chrono> +#include <iomanip> #include <thread> #include <android-base/file.h> @@ -282,7 +283,14 @@ int Dumpsys::main(int argc, char* const argv[]) { std::chrono::duration<double> elapsed_seconds = std::chrono::steady_clock::now() - start; aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str() - << "was the duration of dumpsys " << service_name << endl; + << "was the duration of dumpsys " << service_name; + + using std::chrono::system_clock; + const auto finish = system_clock::to_time_t(system_clock::now()); + std::tm finish_tm; + localtime_r(&finish, &finish_tm); + aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S") + << endl; } } else { aerr << "Can't find service: " << service_name << endl; diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 5ca2b578a6..16fefe64ba 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -35,6 +35,7 @@ using ::testing::DoAll; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::MakeAction; +using ::testing::Mock; using ::testing::Not; using ::testing::Return; using ::testing::StrEq; @@ -155,10 +156,11 @@ class DumpsysTest : public Test { .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0))); } - void ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) { + sp<BinderMock> ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) { sp<BinderMock> binder_mock = ExpectCheckService(name); EXPECT_CALL(*binder_mock, dump(_, _)) .WillRepeatedly(DoAll(Sleep(timeout_s), WithArg<0>(WriteOnFd(output)), Return(0))); + return binder_mock; } void CallMain(const std::vector<std::string>& args) { @@ -245,15 +247,15 @@ TEST_F(DumpsysTest, DumpRunningService) { // Tests 'dumpsys -t 1 service_name' on a service that times out after 2s TEST_F(DumpsysTest, DumpRunningServiceTimeout) { - ExpectDumpAndHang("Valet", 2, "Here's your car"); + sp<BinderMock> binder_mock = ExpectDumpAndHang("Valet", 2, "Here's your car"); CallMain({"-t", "1", "Valet"}); AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1s) EXPIRED"); AssertNotDumped("Here's your car"); - // Must wait so binder mock is deleted, otherwise test will fail with a leaked object - sleep(1); + // TODO(b/65056227): BinderMock is not destructed because thread is detached on dumpsys.cpp + Mock::AllowLeak(binder_mock.get()); } // Tests 'dumpsys service_name Y U NO HAVE ARGS' on a service that is running diff --git a/data/etc/android.hardware.radio.xml b/data/etc/android.hardware.broadcastradio.xml index f718c47cab..c66951805c 100644 --- a/data/etc/android.hardware.radio.xml +++ b/data/etc/android.hardware.broadcastradio.xml @@ -16,5 +16,5 @@ <!-- This is the standard set of features for a broadcast radio. --> <permissions> - <feature name="android.hardware.radio" /> + <feature name="android.hardware.broadcastradio" /> </permissions> diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 835504fc5a..561f5ba9a6 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -41,7 +41,6 @@ <feature name="android.software.voice_recognizers" notLowRam="true" /> <feature name="android.software.backup" /> <feature name="android.software.home_screen" /> - <feature name="android.software.input_methods" /> <feature name="android.software.print" /> <!-- Feature to specify if the device supports adding device admins. --> diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml index 0d5d206438..ec7be53bfc 100644 --- a/data/etc/handheld_core_hardware.xml +++ b/data/etc/handheld_core_hardware.xml @@ -45,7 +45,7 @@ <feature name="android.software.backup" /> <feature name="android.software.home_screen" /> <feature name="android.software.input_methods" /> - <feature name="android.software.picture_in_picture" /> + <feature name="android.software.picture_in_picture" notLowRam="true" /> <feature name="android.software.activities_on_secondary_displays" /> <feature name="android.software.print" /> <feature name="android.software.companion_device_setup" /> diff --git a/headers/Android.bp b/headers/Android.bp new file mode 100644 index 0000000000..82bc8a15f7 --- /dev/null +++ b/headers/Android.bp @@ -0,0 +1,20 @@ +cc_library_headers { + name: "media_plugin_headers", + vendor_available: true, + export_include_dirs: [ + "media_plugin", + "media_plugin/media/openmax", + ], + header_libs: [ + "libstagefright_headers", + "libcutils_headers", + "libutils_headers", + "libstagefright_foundation_headers", + ], + export_header_lib_headers: [ + "libstagefright_headers", + "libcutils_headers", + "libutils_headers", + "libstagefright_foundation_headers", + ], +} diff --git a/include/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h index 67f4511e65..67f4511e65 100644 --- a/include/media/cas/CasAPI.h +++ b/headers/media_plugin/media/cas/CasAPI.h diff --git a/include/media/cas/DescramblerAPI.h b/headers/media_plugin/media/cas/DescramblerAPI.h index 0a519525e0..0a519525e0 100644 --- a/include/media/cas/DescramblerAPI.h +++ b/headers/media_plugin/media/cas/DescramblerAPI.h diff --git a/include/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h index 985d919220..985d919220 100644 --- a/include/media/drm/DrmAPI.h +++ b/headers/media_plugin/media/drm/DrmAPI.h diff --git a/include/media/editor/II420ColorConverter.h b/headers/media_plugin/media/editor/II420ColorConverter.h index 33af61ff9a..33af61ff9a 100644 --- a/include/media/editor/II420ColorConverter.h +++ b/headers/media_plugin/media/editor/II420ColorConverter.h diff --git a/include/media/hardware/CryptoAPI.h b/headers/media_plugin/media/hardware/CryptoAPI.h index 0e86aacc6f..0e86aacc6f 100644 --- a/include/media/hardware/CryptoAPI.h +++ b/headers/media_plugin/media/hardware/CryptoAPI.h diff --git a/include/media/hardware/HDCPAPI.h b/headers/media_plugin/media/hardware/HDCPAPI.h index 7797bb2ab4..7797bb2ab4 100644 --- a/include/media/hardware/HDCPAPI.h +++ b/headers/media_plugin/media/hardware/HDCPAPI.h diff --git a/include/media/hardware/HardwareAPI.h b/headers/media_plugin/media/hardware/HardwareAPI.h index 6c1ba3de00..6c1ba3de00 100644 --- a/include/media/hardware/HardwareAPI.h +++ b/headers/media_plugin/media/hardware/HardwareAPI.h diff --git a/include/media/hardware/MetadataBufferType.h b/headers/media_plugin/media/hardware/MetadataBufferType.h index 4f6d5e2f26..4f6d5e2f26 100644 --- a/include/media/hardware/MetadataBufferType.h +++ b/headers/media_plugin/media/hardware/MetadataBufferType.h diff --git a/include/media/hardware/OMXPluginBase.h b/headers/media_plugin/media/hardware/OMXPluginBase.h index 7bf414739b..7bf414739b 100644 --- a/include/media/hardware/OMXPluginBase.h +++ b/headers/media_plugin/media/hardware/OMXPluginBase.h diff --git a/include/media/hardware/VideoAPI.h b/headers/media_plugin/media/hardware/VideoAPI.h index a09087698c..a09087698c 100644 --- a/include/media/hardware/VideoAPI.h +++ b/headers/media_plugin/media/hardware/VideoAPI.h diff --git a/include/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h index 56d7cc81ce..dc25deddbb 100644 --- a/include/media/openmax/OMX_AsString.h +++ b/headers/media_plugin/media/openmax/OMX_AsString.h @@ -930,6 +930,14 @@ inline static const char *asString(OMX_VIDEO_AVCLOOPFILTERTYPE i, const char *de #ifndef AS_STRING_FOR_OMX_VIDEOEXT_H #define AS_STRING_FOR_OMX_VIDEOEXT_H +inline static const char *asString(OMX_VIDEO_AVCPROFILEEXTTYPE i, const char *def = "??") { + switch (i) { + case OMX_VIDEO_AVCProfileConstrainedBaseline: return "ConstrainedBaseline"; + case OMX_VIDEO_AVCProfileConstrainedHigh: return "ConstrainedHigh"; + default: return asString((OMX_VIDEO_AVCPROFILETYPE)i, def); + } +} + inline static const char *asString(OMX_VIDEO_VP8PROFILETYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_VP8ProfileMain: return "Main"; diff --git a/include/media/openmax/OMX_Audio.h b/headers/media_plugin/media/openmax/OMX_Audio.h index 9c0296bf55..9c0296bf55 100644 --- a/include/media/openmax/OMX_Audio.h +++ b/headers/media_plugin/media/openmax/OMX_Audio.h diff --git a/include/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h index 05c223212e..05c223212e 100644 --- a/include/media/openmax/OMX_AudioExt.h +++ b/headers/media_plugin/media/openmax/OMX_AudioExt.h diff --git a/include/media/openmax/OMX_Component.h b/headers/media_plugin/media/openmax/OMX_Component.h index 0dc2c76972..0dc2c76972 100644 --- a/include/media/openmax/OMX_Component.h +++ b/headers/media_plugin/media/openmax/OMX_Component.h diff --git a/include/media/openmax/OMX_ContentPipe.h b/headers/media_plugin/media/openmax/OMX_ContentPipe.h index 0224c8a2ee..0224c8a2ee 100644 --- a/include/media/openmax/OMX_ContentPipe.h +++ b/headers/media_plugin/media/openmax/OMX_ContentPipe.h diff --git a/include/media/openmax/OMX_Core.h b/headers/media_plugin/media/openmax/OMX_Core.h index bb974b3f88..bb974b3f88 100644 --- a/include/media/openmax/OMX_Core.h +++ b/headers/media_plugin/media/openmax/OMX_Core.h diff --git a/include/media/openmax/OMX_IVCommon.h b/headers/media_plugin/media/openmax/OMX_IVCommon.h index f9b6f4b0fd..f9b6f4b0fd 100644 --- a/include/media/openmax/OMX_IVCommon.h +++ b/headers/media_plugin/media/openmax/OMX_IVCommon.h diff --git a/include/media/openmax/OMX_Image.h b/headers/media_plugin/media/openmax/OMX_Image.h index 23a0209f5d..23a0209f5d 100644 --- a/include/media/openmax/OMX_Image.h +++ b/headers/media_plugin/media/openmax/OMX_Image.h diff --git a/include/media/openmax/OMX_Index.h b/headers/media_plugin/media/openmax/OMX_Index.h index 5be1355a96..5be1355a96 100644 --- a/include/media/openmax/OMX_Index.h +++ b/headers/media_plugin/media/openmax/OMX_Index.h diff --git a/include/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h index 5a029d0a4b..5a029d0a4b 100644 --- a/include/media/openmax/OMX_IndexExt.h +++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h diff --git a/include/media/openmax/OMX_Other.h b/headers/media_plugin/media/openmax/OMX_Other.h index 6072ef62c8..6072ef62c8 100644 --- a/include/media/openmax/OMX_Other.h +++ b/headers/media_plugin/media/openmax/OMX_Other.h diff --git a/include/media/openmax/OMX_Types.h b/headers/media_plugin/media/openmax/OMX_Types.h index 515e002213..515e002213 100644 --- a/include/media/openmax/OMX_Types.h +++ b/headers/media_plugin/media/openmax/OMX_Types.h diff --git a/include/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h index 76efac9b3e..76efac9b3e 100644 --- a/include/media/openmax/OMX_Video.h +++ b/headers/media_plugin/media/openmax/OMX_Video.h diff --git a/include/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h index 128dd2d1da..c1025643ef 100644 --- a/include/media/openmax/OMX_VideoExt.h +++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h @@ -58,6 +58,12 @@ typedef struct OMX_NALSTREAMFORMATTYPE{ OMX_NALUFORMATSTYPE eNaluFormat; } OMX_NALSTREAMFORMATTYPE; +/** AVC additional profiles */ +typedef enum OMX_VIDEO_AVCPROFILEEXTTYPE { + OMX_VIDEO_AVCProfileConstrainedBaseline = 0x10000, /**< Constrained baseline profile */ + OMX_VIDEO_AVCProfileConstrainedHigh = 0x80000, /**< Constrained high profile */ +} OMX_VIDEO_AVCPROFILEEXTTYPE; + /** VP8 profiles */ typedef enum OMX_VIDEO_VP8PROFILETYPE { OMX_VIDEO_VP8ProfileMain = 0x01, @@ -164,20 +170,20 @@ typedef enum OMX_VIDEO_VP9PROFILETYPE { /** VP9 levels */ typedef enum OMX_VIDEO_VP9LEVELTYPE { - OMX_VIDEO_VP9Level1 = 0x0, - OMX_VIDEO_VP9Level11 = 0x1, - OMX_VIDEO_VP9Level2 = 0x2, - OMX_VIDEO_VP9Level21 = 0x4, - OMX_VIDEO_VP9Level3 = 0x8, - OMX_VIDEO_VP9Level31 = 0x10, - OMX_VIDEO_VP9Level4 = 0x20, - OMX_VIDEO_VP9Level41 = 0x40, - OMX_VIDEO_VP9Level5 = 0x80, - OMX_VIDEO_VP9Level51 = 0x100, - OMX_VIDEO_VP9Level52 = 0x200, - OMX_VIDEO_VP9Level6 = 0x400, - OMX_VIDEO_VP9Level61 = 0x800, - OMX_VIDEO_VP9Level62 = 0x1000, + OMX_VIDEO_VP9Level1 = 0x1, + OMX_VIDEO_VP9Level11 = 0x2, + OMX_VIDEO_VP9Level2 = 0x4, + OMX_VIDEO_VP9Level21 = 0x8, + OMX_VIDEO_VP9Level3 = 0x10, + OMX_VIDEO_VP9Level31 = 0x20, + OMX_VIDEO_VP9Level4 = 0x40, + OMX_VIDEO_VP9Level41 = 0x80, + OMX_VIDEO_VP9Level5 = 0x100, + OMX_VIDEO_VP9Level51 = 0x200, + OMX_VIDEO_VP9Level52 = 0x400, + OMX_VIDEO_VP9Level6 = 0x800, + OMX_VIDEO_VP9Level61 = 0x1000, + OMX_VIDEO_VP9Level62 = 0x2000, OMX_VIDEO_VP9LevelUnknown = 0x6EFFFFFF, OMX_VIDEO_VP9LevelMax = 0x7FFFFFFF } OMX_VIDEO_VP9LEVELTYPE; @@ -290,6 +296,8 @@ typedef enum OMX_VIDEO_DOLBYVISIONPROFILETYPE { OMX_VIDEO_DolbyVisionProfileDvheStn = 0x20, OMX_VIDEO_DolbyVisionProfileDvheDth = 0x40, OMX_VIDEO_DolbyVisionProfileDvheDtb = 0x80, + OMX_VIDEO_DolbyVisionProfileDvheSt = 0x100, + OMX_VIDEO_DolbyVisionProfileDvavSe = 0x200, OMX_VIDEO_DolbyVisionProfileMax = 0x7FFFFFFF } OMX_VIDEO_DOLBYVISIONPROFILETYPE; diff --git a/include/android/keycodes.h b/include/android/keycodes.h index e202060f16..2164d6163e 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -765,7 +765,9 @@ enum { /** fingerprint navigation key, left. */ AKEYCODE_SYSTEM_NAVIGATION_LEFT = 282, /** fingerprint navigation key, right. */ - AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283 + AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283, + /** all apps */ + AKEYCODE_ALL_APPS = 284 // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/android/sensor.h b/include/android/sensor.h index 2db0ee7fd9..a88733cac7 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -197,7 +197,7 @@ enum { * A sensor event. */ -/* NOTE: changes to these structs have to be backward compatible */ +/* NOTE: Must match hardware/sensors.h */ typedef struct ASensorVector { union { float v[3]; @@ -259,7 +259,7 @@ typedef struct { }; } AAdditionalInfoEvent; -/* NOTE: changes to this struct has to be backward compatible */ +/* NOTE: Must match hardware/sensors.h */ typedef struct ASensorEvent { int32_t version; /* sizeof(struct ASensorEvent) */ int32_t sensor; diff --git a/include/android/sharedmem_jni.h b/include/android/sharedmem_jni.h new file mode 100644 index 0000000000..85ac78f9b1 --- /dev/null +++ b/include/android/sharedmem_jni.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup Memory + * @{ + */ + +/** + * @file sharedmem_jni.h + */ + +#ifndef ANDROID_SHARED_MEMORY_JNI_H +#define ANDROID_SHARED_MEMORY_JNI_H + +#include <jni.h> +#include <android/sharedmem.h> +#include <stddef.h> + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit). + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +/** + * Structures and functions for a shared memory buffer that can be shared across process. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if __ANDROID_API__ >= __ANDROID_API_O_MR1__ + +/** + * Returns a dup'd FD from the given Java android.os.SharedMemory object. The returned file + * descriptor has all the same properties & capabilities as the FD returned from + * ASharedMemory_create(), however the protection flags will be the same as those of the + * android.os.SharedMemory object. + * + * Use close() to release the shared memory region. + * + * \param env The JNIEnv* pointer + * \param sharedMemory The Java android.os.SharedMemory object + * \return file descriptor that denotes the shared memory; -1 if the shared memory object is + * already closed, if the JNIEnv or jobject is NULL, or if there are too many open file + * descriptors (errno=EMFILE) + */ +int ASharedMemory_dupFromJava(JNIEnv* env, jobject sharedMemory); + +#endif + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_SHARED_MEMORY_JNI_H + +/** @} */ diff --git a/include/batteryservice b/include/batteryservice new file mode 120000 index 0000000000..2178c32092 --- /dev/null +++ b/include/batteryservice @@ -0,0 +1 @@ +../services/batteryservice/include/batteryservice/
\ No newline at end of file diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 20154eb10e..c282cf0b22 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -323,6 +323,7 @@ static const InputEventLabel KEYCODES[] = { DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), + DEFINE_KEYCODE(ALL_APPS), { NULL, 0 } }; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index efa1ffbfee..944947420e 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -370,20 +370,24 @@ private: int32_t idToIndex[MAX_POINTER_ID + 1]; PointerCoords pointers[MAX_POINTERS]; - void initializeFrom(const InputMessage* msg) { - eventTime = msg->body.motion.eventTime; + void initializeFrom(const InputMessage& msg) { + eventTime = msg.body.motion.eventTime; idBits.clear(); - for (uint32_t i = 0; i < msg->body.motion.pointerCount; i++) { - uint32_t id = msg->body.motion.pointers[i].properties.id; + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + uint32_t id = msg.body.motion.pointers[i].properties.id; idBits.markBit(id); idToIndex[id] = i; - pointers[i].copyFrom(msg->body.motion.pointers[i].coords); + pointers[i].copyFrom(msg.body.motion.pointers[i].coords); } } const PointerCoords& getPointerById(uint32_t id) const { return pointers[idToIndex[id]]; } + + bool hasPointerId(uint32_t id) const { + return idBits.hasBit(id); + } }; struct TouchState { int32_t deviceId; @@ -402,7 +406,7 @@ private: lastResample.idBits.clear(); } - void addHistory(const InputMessage* msg) { + void addHistory(const InputMessage& msg) { historyCurrent ^= 1; if (historySize < 2) { historySize += 1; @@ -413,6 +417,24 @@ private: const History* getHistory(size_t index) const { return &history[(historyCurrent + index) & 1]; } + + bool recentCoordinatesAreIdentical(uint32_t id) const { + // Return true if the two most recently received "raw" coordinates are identical + if (historySize < 2) { + return false; + } + if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) { + return false; + } + float currentX = getHistory(0)->getPointerById(id).getX(); + float currentY = getHistory(0)->getPointerById(id).getY(); + float previousX = getHistory(1)->getPointerById(id).getX(); + float previousY = getHistory(1)->getPointerById(id).getY(); + if (currentX == previousX && currentY == previousY) { + return true; + } + return false; + } }; Vector<TouchState> mTouchStates; @@ -432,8 +454,8 @@ private: Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId); - void updateTouchState(InputMessage* msg); - void rewriteMessage(const TouchState& state, InputMessage* msg); + void updateTouchState(InputMessage& msg); + bool rewriteMessage(const TouchState& state, InputMessage& msg); void resampleTouchState(nsecs_t frameTime, MotionEvent* event, const InputMessage *next); diff --git a/include/media b/include/media new file mode 120000 index 0000000000..3e7da1c11f --- /dev/null +++ b/include/media @@ -0,0 +1 @@ +../headers/media_plugin/media
\ No newline at end of file diff --git a/include_sensor/android/looper.h b/include_sensor/android/looper.h new file mode 120000 index 0000000000..0cf51b88b5 --- /dev/null +++ b/include_sensor/android/looper.h @@ -0,0 +1 @@ +../../include/android/looper.h
\ No newline at end of file diff --git a/include_sensor/android/sensor.h b/include_sensor/android/sensor.h new file mode 120000 index 0000000000..0626f4f171 --- /dev/null +++ b/include_sensor/android/sensor.h @@ -0,0 +1 @@ +../../include/android/sensor.h
\ No newline at end of file diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 09fd0cb482..83b8021735 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -69,8 +69,13 @@ cc_library { "TextOutput.cpp", "IpPrefix.cpp", "Value.cpp", + "aidl/android/content/pm/IPackageManagerNative.aidl", ], + aidl: { + export_aidl_headers: true, + }, + cflags: [ "-Wall", "-Wextra", diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl new file mode 100644 index 0000000000..3264666a21 --- /dev/null +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -0,0 +1,57 @@ +/* +** +** Copyright 2017, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.content.pm; + +/** + * Parallel implementation of certain {@link PackageManager} APIs that need to + * be exposed to native code. + * <p>These APIs are a parallel definition to the APIs in PackageManager, so, + * they can technically diverge. However, it's good practice to keep these + * APIs in sync with each other. + * <p>Because these APIs are exposed to native code, it's possible they will + * be exposed to privileged components [such as UID 0]. Care should be taken + * to avoid exposing potential security holes for methods where permission + * checks are bypassed based upon UID alone. + * + * @hide + */ +interface IPackageManagerNative { + /** + * Returns a set of names for the given UIDs. + * IMPORTANT: Unlike the Java version of this API, unknown UIDs are + * not represented by 'null's. Instead, they are represented by empty + * strings. + */ + @utf8InCpp String[] getNamesForUids(in int[] uids); + + /** + * Returns the name of the installer (a package) which installed the named + * package. Preloaded packages return the string "preload". Sideloaded packages + * return an empty string. Unknown or unknowable are returned as empty strings. + */ + + @utf8InCpp String getInstallerForPackage(in String packageName); + + /** + * Returns the version code of the named package. + * Unknown or unknowable versions are returned as 0. + */ + + int getVersionCodeForPackage(in String packageName); + +} diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 549cdb89ae..cf72d55894 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -99,6 +99,7 @@ cc_library_shared { "IProducerListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", + "LayerDebugInfo.cpp", "LayerState.cpp", "OccupancyTracker.cpp", "StreamSplitter.cpp", diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index d9d50dbeb4..da4295609b 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -19,6 +19,8 @@ //#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <utils/Log.h> +#include <inttypes.h> + #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> @@ -31,13 +33,13 @@ namespace android { BufferItemConsumer::BufferItemConsumer( - const sp<IGraphicBufferConsumer>& consumer, uint32_t consumerUsage, + const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage, int bufferCount, bool controlledByApp) : ConsumerBase(consumer, controlledByApp) { status_t err = mConsumer->setConsumerUsageBits(consumerUsage); LOG_ALWAYS_FATAL_IF(err != OK, - "Failed to set consumer usage bits to %#x", consumerUsage); + "Failed to set consumer usage bits to %#" PRIx64, consumerUsage); if (bufferCount != DEFAULT_MAX_BUFFERS) { err = mConsumer->setMaxAcquiredBufferCount(bufferCount); LOG_ALWAYS_FATAL_IF(err != OK, diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 31f3324d23..c5cab2d730 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -347,10 +347,10 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, return NO_ERROR; } -status_t BufferQueueProducer::dequeueBuffer(int *outSlot, - sp<android::Fence> *outFence, uint32_t width, uint32_t height, - PixelFormat format, uint64_t usage, - FrameEventHistoryDelta* outTimestamps) { +status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence, + uint32_t width, uint32_t height, PixelFormat format, + uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) { ATRACE_CALL(); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); @@ -558,6 +558,9 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, mSlots[*outSlot].mFrameNumber, mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); + if (outBufferAge) { + *outBufferAge = mCore->mBufferAge; + } addAndGetFrameTimestamps(nullptr, outTimestamps); return returnFlags; @@ -1099,6 +1102,7 @@ int BufferQueueProducer::query(int what, int *outValue) { value = (mCore->mQueue.size() > 1); break; case NATIVE_WINDOW_CONSUMER_USAGE_BITS: + // deprecated; higher 32 bits are truncated value = static_cast<int32_t>(mCore->mConsumerUsageBits); break; case NATIVE_WINDOW_DEFAULT_DATASPACE: @@ -1544,4 +1548,12 @@ status_t BufferQueueProducer::getUniqueId(uint64_t* outId) const { return NO_ERROR; } +status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const { + BQ_LOGV("getConsumerUsage"); + + Mutex::Autolock lock(mCore->mMutex); + *outUsage = mCore->mConsumerUsageBits; + return NO_ERROR; +} + } // namespace android diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 679c70af4c..14d9937142 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -1115,7 +1115,7 @@ status_t GLConsumer::setDefaultBufferDataSpace( return mConsumer->setDefaultBufferDataSpace(defaultDataSpace); } -status_t GLConsumer::setConsumerUsageBits(uint32_t usage) { +status_t GLConsumer::setConsumerUsageBits(uint64_t usage) { Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("setConsumerUsageBits: GLConsumer is abandoned!"); diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 1b0fe06810..71e22cedf0 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -62,7 +62,8 @@ enum { SET_DEQUEUE_TIMEOUT, GET_LAST_QUEUED_BUFFER, GET_FRAME_TIMESTAMPS, - GET_UNIQUE_ID + GET_UNIQUE_ID, + GET_CONSUMER_USAGE, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -124,9 +125,9 @@ public: return result; } - virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width, - uint32_t height, PixelFormat format, uint64_t usage, - FrameEventHistoryDelta* outTimestamps) { + virtual status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) { Parcel data, reply; bool getFrameTimestamps = (outTimestamps != nullptr); @@ -149,6 +150,17 @@ public: fence->clear(); return result; } + if (outBufferAge) { + result = reply.readUint64(outBufferAge); + } else { + // Read the value even if outBufferAge is nullptr: + uint64_t bufferAge; + result = reply.readUint64(&bufferAge); + } + if (result != NO_ERROR) { + ALOGE("IGBP::dequeueBuffer failed to read buffer age: %d", result); + return result; + } if (getFrameTimestamps) { result = reply.read(*outTimestamps); if (result != NO_ERROR) { @@ -493,6 +505,25 @@ public: } return actualResult; } + + virtual status_t getConsumerUsage(uint64_t* outUsage) const { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + status_t result = remote()->transact(GET_CONSUMER_USAGE, data, &reply); + if (result != NO_ERROR) { + ALOGE("getConsumerUsage failed to transact: %d", result); + } + status_t actualResult = NO_ERROR; + result = reply.readInt32(&actualResult); + if (result != NO_ERROR) { + return result; + } + result = reply.readUint64(outUsage); + if (result != NO_ERROR) { + return result; + } + return actualResult; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -516,11 +547,10 @@ public: return mBase->setAsyncMode(async); } - status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, - PixelFormat format, uint64_t usage, - FrameEventHistoryDelta* outTimestamps) override { - return mBase->dequeueBuffer( - slot, fence, w, h, format, usage, outTimestamps); + status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, PixelFormat format, + uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) override { + return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps); } status_t detachBuffer(int slot) override { @@ -612,6 +642,10 @@ public: status_t getUniqueId(uint64_t* outId) const override { return mBase->getUniqueId(outId); } + + status_t getConsumerUsage(uint64_t* outUsage) const override { + return mBase->getConsumerUsage(outUsage); + } }; IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer, @@ -655,16 +689,18 @@ status_t BnGraphicBufferProducer::onTransact( uint32_t height = data.readUint32(); PixelFormat format = static_cast<PixelFormat>(data.readInt32()); uint64_t usage = data.readUint64(); + uint64_t bufferAge = 0; bool getTimestamps = data.readBool(); int buf = 0; sp<Fence> fence = Fence::NO_FENCE; FrameEventHistoryDelta frameTimestamps; - int result = dequeueBuffer(&buf, &fence, width, height, format, - usage, getTimestamps ? &frameTimestamps : nullptr); + int result = dequeueBuffer(&buf, &fence, width, height, format, usage, &bufferAge, + getTimestamps ? &frameTimestamps : nullptr); reply->writeInt32(buf); reply->write(*fence); + reply->writeUint64(bufferAge); if (getTimestamps) { reply->write(frameTimestamps); } @@ -877,6 +913,20 @@ status_t BnGraphicBufferProducer::onTransact( } return NO_ERROR; } + case GET_CONSUMER_USAGE: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + uint64_t outUsage = 0; + status_t actualResult = getConsumerUsage(&outUsage); + status_t result = reply->writeInt32(actualResult); + if (result != NO_ERROR) { + return result; + } + result = reply->writeUint64(outUsage); + if (result != NO_ERROR) { + return result; + } + return NO_ERROR; + } } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 0a0d112af6..8e7f814313 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -28,6 +28,7 @@ #include <gui/IGraphicBufferProducer.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> +#include <gui/LayerDebugInfo.h> #include <private/gui/LayerState.h> @@ -469,6 +470,36 @@ public: return result; } + virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const + { + if (!outLayers) { + return UNEXPECTED_NULL; + } + + Parcel data, reply; + + status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + return err; + } + + err = remote()->transact(BnSurfaceComposer::GET_LAYER_DEBUG_INFO, data, &reply); + if (err != NO_ERROR) { + return err; + } + + int32_t result = 0; + err = reply.readInt32(&result); + if (err != NO_ERROR) { + return err; + } + if (result != NO_ERROR) { + return result; + } + + outLayers->clear(); + return reply.readParcelableVector(outLayers); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -763,6 +794,17 @@ status_t BnSurfaceComposer::onTransact( } return injectVSync(when); } + case GET_LAYER_DEBUG_INFO: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + std::vector<LayerDebugInfo> outLayers; + status_t result = getLayerDebugInfo(&outLayers); + reply->writeInt32(result); + if (result == NO_ERROR) + { + result = reply->writeParcelableVector(outLayers); + } + return result; + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp new file mode 100644 index 0000000000..57ddde075a --- /dev/null +++ b/libs/gui/LayerDebugInfo.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/LayerDebugInfo.h> + +#include <ui/DebugUtils.h> + +#include <binder/Parcel.h> + +#include <utils/String8.h> + +using namespace android; + +#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false) + +namespace android { + +status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { + RETURN_ON_ERROR(parcel->writeCString(mName.c_str())); + RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str())); + RETURN_ON_ERROR(parcel->writeCString(mType.c_str())); + RETURN_ON_ERROR(parcel->write(mTransparentRegion)); + RETURN_ON_ERROR(parcel->write(mVisibleRegion)); + RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion)); + RETURN_ON_ERROR(parcel->writeUint32(mLayerStack)); + RETURN_ON_ERROR(parcel->writeFloat(mX)); + RETURN_ON_ERROR(parcel->writeFloat(mY)); + RETURN_ON_ERROR(parcel->writeUint32(mZ)); + RETURN_ON_ERROR(parcel->writeInt32(mWidth)); + RETURN_ON_ERROR(parcel->writeInt32(mHeight)); + RETURN_ON_ERROR(parcel->write(mCrop)); + RETURN_ON_ERROR(parcel->write(mFinalCrop)); + RETURN_ON_ERROR(parcel->writeFloat(mAlpha)); + RETURN_ON_ERROR(parcel->writeUint32(mFlags)); + RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat)); + RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace))); + for (size_t index = 0; index < 4; index++) { + RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2])); + } + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat)); + RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames)); + RETURN_ON_ERROR(parcel->writeBool(mRefreshPending)); + RETURN_ON_ERROR(parcel->writeBool(mIsOpaque)); + RETURN_ON_ERROR(parcel->writeBool(mContentDirty)); + return NO_ERROR; +} + +status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { + mName = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + mParentName = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + mType = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + RETURN_ON_ERROR(parcel->read(mTransparentRegion)); + RETURN_ON_ERROR(parcel->read(mVisibleRegion)); + RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion)); + RETURN_ON_ERROR(parcel->readUint32(&mLayerStack)); + RETURN_ON_ERROR(parcel->readFloat(&mX)); + RETURN_ON_ERROR(parcel->readFloat(&mY)); + RETURN_ON_ERROR(parcel->readUint32(&mZ)); + RETURN_ON_ERROR(parcel->readInt32(&mWidth)); + RETURN_ON_ERROR(parcel->readInt32(&mHeight)); + RETURN_ON_ERROR(parcel->read(mCrop)); + RETURN_ON_ERROR(parcel->read(mFinalCrop)); + RETURN_ON_ERROR(parcel->readFloat(&mAlpha)); + RETURN_ON_ERROR(parcel->readUint32(&mFlags)); + RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat)); + // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways? + mDataSpace = static_cast<android_dataspace>(parcel->readUint32()); + RETURN_ON_ERROR(parcel->errorCheck()); + for (size_t index = 0; index < 4; index++) { + RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2])); + } + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat)); + RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames)); + RETURN_ON_ERROR(parcel->readBool(&mRefreshPending)); + RETURN_ON_ERROR(parcel->readBool(&mIsOpaque)); + RETURN_ON_ERROR(parcel->readBool(&mContentDirty)); + return NO_ERROR; +} + +std::string to_string(const LayerDebugInfo& info) { + String8 result; + + result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); + info.mTransparentRegion.dump(result, "TransparentRegion"); + info.mVisibleRegion.dump(result, "VisibleRegion"); + info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); + + result.appendFormat(" layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", + info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY), + info.mWidth, info.mHeight); + + result.appendFormat("crop=%s, finalCrop=%s, ", + to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str()); + result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); + result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); + result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); + result.appendFormat("alpha=%.3f, flags=0x%08x, ", + static_cast<double>(info.mAlpha), info.mFlags); + result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]", + static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]), + static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1])); + result.append("\n"); + result.appendFormat(" parent=%s\n", info.mParentName.c_str()); + result.appendFormat(" activeBuffer=[%4ux%4u:%4u,%s],", + info.mActiveBufferWidth, info.mActiveBufferHeight, + info.mActiveBufferStride, + decodePixelFormat(info.mActiveBufferFormat).c_str()); + result.appendFormat(" queued-frames=%d, mRefreshPending=%d", + info.mNumQueuedFrames, info.mRefreshPending); + result.append("\n"); + return std::string(result.c_str()); +} + +} // android diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 05ae7bb63f..d9d945dff1 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -44,21 +44,19 @@ namespace android { -Surface::Surface( - const sp<IGraphicBufferProducer>& bufferProducer, - bool controlledByApp) - : mGraphicBufferProducer(bufferProducer), - mCrop(Rect::EMPTY_RECT), - mGenerationNumber(0), - mSharedBufferMode(false), - mAutoRefresh(false), - mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT), - mSharedBufferHasBeenQueued(false), - mQueriedSupportedTimestamps(false), - mFrameTimestampsSupportsPresent(false), - mEnableFrameTimestamps(false), - mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) -{ +Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp) + : mGraphicBufferProducer(bufferProducer), + mCrop(Rect::EMPTY_RECT), + mBufferAge(0), + mGenerationNumber(0), + mSharedBufferMode(false), + mAutoRefresh(false), + mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT), + mSharedBufferHasBeenQueued(false), + mQueriedSupportedTimestamps(false), + mFrameTimestampsSupportsPresent(false), + mEnableFrameTimestamps(false), + mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; ANativeWindow::dequeueBuffer = hook_dequeueBuffer; @@ -509,9 +507,10 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { nsecs_t startTime = systemTime(); FrameEventHistoryDelta frameTimestamps; - status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, - reqWidth, reqHeight, reqFormat, reqUsage, - enableFrameTimestamps ? &frameTimestamps : nullptr); + status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, + reqFormat, reqUsage, &mBufferAge, + enableFrameTimestamps ? &frameTimestamps + : nullptr); mLastDequeueDuration = systemTime() - startTime; if (result < 0) { @@ -848,6 +847,14 @@ int Surface::query(int what, int* value) const { } return err; } + case NATIVE_WINDOW_BUFFER_AGE: { + if (mBufferAge > INT32_MAX) { + *value = 0; + } else { + *value = static_cast<int32_t>(mBufferAge); + } + return NO_ERROR; + } case NATIVE_WINDOW_LAST_DEQUEUE_DURATION: { int64_t durationUs = mLastDequeueDuration / 1000; *value = durationUs > std::numeric_limits<int>::max() ? @@ -970,6 +977,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_SET_USAGE64: res = dispatchSetUsage64(args); break; + case NATIVE_WINDOW_GET_CONSUMER_USAGE64: + res = dispatchGetConsumerUsage64(args); + break; default: res = NAME_NOT_FOUND; break; @@ -1148,6 +1158,11 @@ int Surface::dispatchGetHdrSupport(va_list args) { return getHdrSupport(outSupport); } +int Surface::dispatchGetConsumerUsage64(va_list args) { + uint64_t* usage = va_arg(args, uint64_t*); + return getConsumerUsage(usage); +} + int Surface::connect(int api) { static sp<IProducerListener> listener = new DummyProducerListener(); return connect(api, listener); @@ -1717,6 +1732,11 @@ status_t Surface::getUniqueId(uint64_t* outId) const { return mGraphicBufferProducer->getUniqueId(outId); } +int Surface::getConsumerUsage(uint64_t* outUsage) const { + Mutex::Autolock lock(mMutex); + return mGraphicBufferProducer->getConsumerUsage(outUsage); +} + nsecs_t Surface::getLastDequeueStartTime() const { Mutex::Autolock lock(mMutex); return mLastDequeueStartTime; diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp index 7c0552e0dc..3b89291dc8 100644 --- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp @@ -21,6 +21,8 @@ #include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> #include <gui/bufferqueue/1.0/B2HProducerListener.h> +#include <system/window.h> + namespace android { namespace hardware { namespace graphics { @@ -989,10 +991,10 @@ status_t H2BGraphicBufferProducer::setAsyncMode(bool async) { } // FIXME: usage bits truncated -- needs a 64-bits usage version -status_t H2BGraphicBufferProducer::dequeueBuffer( - int* slot, sp<Fence>* fence, - uint32_t w, uint32_t h, ::android::PixelFormat format, - uint64_t usage, FrameEventHistoryDelta* outTimestamps) { +status_t H2BGraphicBufferProducer::dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, + uint32_t h, ::android::PixelFormat format, + uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) { *fence = new Fence(); status_t fnStatus; status_t transStatus = toStatusT(mBase->dequeueBuffer( @@ -1016,6 +1018,10 @@ status_t H2BGraphicBufferProducer::dequeueBuffer( fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } })); + if (outBufferAge) { + // Since the HAL version doesn't return the buffer age, set it to 0: + *outBufferAge = 0; + } return transStatus == NO_ERROR ? fnStatus : transStatus; } @@ -1228,6 +1234,18 @@ status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const { return transStatus == NO_ERROR ? fnStatus : transStatus; } +status_t H2BGraphicBufferProducer::getConsumerUsage(uint64_t* outUsage) const { + ALOGW("getConsumerUsage is not fully supported"); + int result; + status_t transStatus = toStatusT(mBase->query( + NATIVE_WINDOW_CONSUMER_USAGE_BITS, + [&result, outUsage] (int32_t tResult, int32_t tValue) { + result = static_cast<int>(tResult); + *outUsage = static_cast<uint64_t>(tValue); + })); + return transStatus == NO_ERROR ? result : static_cast<int>(transStatus); +} + } // namespace utils } // namespace V1_0 } // namespace bufferqueue diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h index 217fe6ad81..d9c57757f5 100644 --- a/libs/gui/include/gui/BufferItemConsumer.h +++ b/libs/gui/include/gui/BufferItemConsumer.h @@ -52,7 +52,7 @@ class BufferItemConsumer: public ConsumerBase // controlledByApp tells whether this consumer is controlled by the // application. BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, - uint32_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS, + uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false); ~BufferItemConsumer() override; diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index 0f8917aa6d..5c7ffb416d 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -80,9 +80,10 @@ public: // // In both cases, the producer will need to call requestBuffer to get a // GraphicBuffer handle for the returned slot. - virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, - uint32_t width, uint32_t height, PixelFormat format, - uint64_t usage, FrameEventHistoryDelta* outTimestamps) override; + virtual status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width, + uint32_t height, PixelFormat format, uint64_t usage, + uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) override; // See IGraphicBufferProducer::detachBuffer virtual status_t detachBuffer(int slot); @@ -182,6 +183,9 @@ public: // See IGraphicBufferProducer::getUniqueId virtual status_t getUniqueId(uint64_t* outId) const override; + // See IGraphicBufferProducer::getConsumerUsage + virtual status_t getConsumerUsage(uint64_t* outUsage) const override; + private: // This is required by the IBinder::DeathRecipient interface virtual void binderDied(const wp<IBinder>& who); diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 2cf6162fd8..75f2ccaaea 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -210,7 +210,7 @@ public: // so the refactoring can proceed smoothly status_t setDefaultBufferFormat(PixelFormat defaultFormat); status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace); - status_t setConsumerUsageBits(uint32_t usage); + status_t setConsumerUsageBits(uint64_t usage); status_t setTransformHint(uint32_t hint); status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); @@ -386,7 +386,7 @@ private: // BufferQueue instance; these will be OR:d with any additional flags passed // from the GLConsumer user. In particular, GLConsumer will always // consume buffers as hardware textures. - static const uint32_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; // mCurrentTextureImage is the EglImage/buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 6d16e7426c..039dc0d657 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -194,9 +194,9 @@ public: // // All other negative values are an unknown error returned downstream // from the graphics allocator (typically errno). - virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, - uint32_t h, PixelFormat format, uint64_t usage, - FrameEventHistoryDelta* outTimestamps) = 0; + virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, + PixelFormat format, uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) = 0; // detachBuffer attempts to remove all ownership of the buffer in the given // slot from the buffer queue. If this call succeeds, the slot will be @@ -593,6 +593,12 @@ public: // Returns a unique id for this BufferQueue virtual status_t getUniqueId(uint64_t* outId) const = 0; + + // Returns the consumer usage flags for this BufferQueue. This returns the + // full 64-bit usage flags, rather than the truncated 32-bit usage flags + // returned by querying the now deprecated + // NATIVE_WINDOW_CONSUMER_USAGE_BITS attribute. + virtual status_t getConsumerUsage(uint64_t* outUsage) const = 0; }; // ---------------------------------------------------------------------------- diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index f80ba000b4..b2267426a8 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -39,6 +39,7 @@ struct ComposerState; struct DisplayState; struct DisplayInfo; struct DisplayStatInfo; +class LayerDebugInfo; class HdrCapabilities; class IDisplayEventConnection; class IGraphicBufferProducer; @@ -195,6 +196,12 @@ public: virtual status_t enableVSyncInjections(bool enable) = 0; virtual status_t injectVSync(nsecs_t when) = 0; + + /* Gets the list of active layers in Z order for debugging purposes + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0; }; // ---------------------------------------------------------------------------- @@ -229,6 +236,7 @@ public: SET_ACTIVE_COLOR_MODE, ENABLE_VSYNC_INJECTIONS, INJECT_VSYNC, + GET_LAYER_DEBUG_INFO, CREATE_SCOPED_CONNECTION }; diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h new file mode 100644 index 0000000000..8453e043ef --- /dev/null +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/Parcelable.h> + +#include <ui/PixelFormat.h> +#include <ui/Region.h> + +#include <string> + +namespace android { + +/* Class for transporting debug info from SurfaceFlinger to authorized + * recipients. The class is intended to be a data container. There are + * no getters or setters. + */ +class LayerDebugInfo : public Parcelable { +public: + LayerDebugInfo() = default; + LayerDebugInfo(const LayerDebugInfo&) = default; + virtual ~LayerDebugInfo() = default; + + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + + std::string mName = std::string("NOT FILLED"); + std::string mParentName = std::string("NOT FILLED"); + std::string mType = std::string("NOT FILLED"); + Region mTransparentRegion = Region::INVALID_REGION; + Region mVisibleRegion = Region::INVALID_REGION; + Region mSurfaceDamageRegion = Region::INVALID_REGION; + uint32_t mLayerStack = 0; + float mX = 0.f; + float mY = 0.f; + uint32_t mZ = 0 ; + int32_t mWidth = -1; + int32_t mHeight = -1; + Rect mCrop = Rect::INVALID_RECT; + Rect mFinalCrop = Rect::INVALID_RECT; + float mAlpha = 0.f; + uint32_t mFlags = 0; + PixelFormat mPixelFormat = PIXEL_FORMAT_NONE; + android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN; + // Row-major transform matrix (SurfaceControl::setMatrix()) + float mMatrix[2][2] = {{0.f, 0.f}, {0.f, 0.f}}; + int32_t mActiveBufferWidth = -1; + int32_t mActiveBufferHeight = -1; + int32_t mActiveBufferStride = 0; + PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE; + int32_t mNumQueuedFrames = -1; + bool mRefreshPending = false; + bool mIsOpaque = false; + bool mContentDirty = false; +}; + +std::string to_string(const LayerDebugInfo& info); + +} // namespace android diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 0f7e12a228..55dd6bf067 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -159,6 +159,7 @@ public: status_t getHdrSupport(bool* supported); status_t getUniqueId(uint64_t* outId) const; + status_t getConsumerUsage(uint64_t* outUsage) const; // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call nsecs_t getLastDequeueStartTime() const; @@ -223,6 +224,7 @@ private: int dispatchGetFrameTimestamps(va_list args); int dispatchGetWideColorSupport(va_list args); int dispatchGetHdrSupport(va_list args); + int dispatchGetConsumerUsage64(va_list args); protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); @@ -403,6 +405,10 @@ protected: // (the change since the previous frame) passed in by the producer. Region mDirtyRegion; + // mBufferAge tracks the age of the contents of the most recently dequeued + // buffer as the number of frames that have elapsed since it was last queued + uint64_t mBufferAge; + // Stores the current generation number. See setGenerationNumber and // IGraphicBufferProducer::setGenerationNumber for more information. uint32_t mGenerationNumber; diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index 8bb705cf77..c15209d32c 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -90,6 +90,16 @@ public: status_t setFlags(uint32_t flags, uint32_t mask); status_t setTransparentRegionHint(const Region& transparent); status_t setAlpha(float alpha=1.0f); + + // Experimentarily it appears that the matrix transforms the + // on-screen rectangle and it's contents before the position is + // applied. + // + // TODO: Test with other combinations to find approximate transformation rules. + // + // For example: + // Layer sized (W,H) set to position (x,y) with matrix M=[-1, 0, 0, 1] (Horizontal flip) gives + // [((0, 0), (W, H)) x M] + (x,y) = ((-W, 0), (0, H)) + (x,y) = ((-W + x, y), (x, H+y)) status_t setMatrix(float dsdx, float dtdx, float dtdy, float dsdy); status_t setCrop(const Rect& crop); status_t setFinalCrop(const Rect& crop); diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h index c3a9d443ec..74850b4879 100644 --- a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h +++ b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h @@ -64,9 +64,9 @@ struct H2BGraphicBufferProducer : public ::android::H2BConverter< status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override; status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override; status_t setAsyncMode(bool async) override; - status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, - uint32_t h, ::android::PixelFormat format, uint64_t usage, - FrameEventHistoryDelta* outTimestamps) override; + status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, + ::android::PixelFormat format, uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) override; status_t detachBuffer(int slot) override; status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override; @@ -94,6 +94,7 @@ struct H2BGraphicBufferProducer : public ::android::H2BConverter< sp<Fence>* outFence, float outTransformMatrix[16]) override; void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override; status_t getUniqueId(uint64_t* outId) const override; + status_t getConsumerUsage(uint64_t* outUsage) const override; }; } // namespace utils diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp index d64e530488..b87cbbdec8 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -76,8 +76,8 @@ class BufferItemConsumerTest : public ::testing::Test { int slot; sp<Fence> outFence; - status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth, - kHeight, 0, 0, nullptr); + status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth, kHeight, 0, 0, + nullptr, nullptr); ASSERT_GE(ret, 0); ALOGV("dequeueBuffer: slot=%d", slot); diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 4220aafa07..9a208593ab 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -144,8 +144,8 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; @@ -188,16 +188,16 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); } ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); @@ -239,8 +239,8 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(3)); for (int i = 0; i < 3; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -275,8 +275,8 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { BufferItem item; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -285,8 +285,8 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, - GRALLOC_USAGE_SW_READ_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -335,8 +335,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->detachBuffer(slot)); @@ -384,8 +384,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), @@ -420,8 +420,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { EGL_NO_SYNC_KHR, Fence::NO_FENCE)); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataOut; @@ -443,8 +443,8 @@ TEST_F(BufferQueueTest, MoveFromConsumerToProducer) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; @@ -492,22 +492,24 @@ TEST_F(BufferQueueTest, TestDisallowingAllocation) { sp<GraphicBuffer> buffer; // This should return an error since it would require an allocation ASSERT_EQ(OK, mProducer->allowAllocation(false)); - ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, - 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + ASSERT_EQ(WOULD_BLOCK, + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); // This should succeed, now that we've lifted the prohibition ASSERT_EQ(OK, mProducer->allowAllocation(true)); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); // Release the previous buffer back to the BufferQueue mProducer->cancelBuffer(slot, fence); // This should fail since we're requesting a different size ASSERT_EQ(OK, mProducer->allowAllocation(false)); - ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, - WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + ASSERT_EQ(WOULD_BLOCK, + mProducer->dequeueBuffer(&slot, &fence, WIDTH * 2, HEIGHT * 2, 0, + GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); } TEST_F(BufferQueueTest, TestGenerationNumbers) { @@ -524,7 +526,7 @@ TEST_F(BufferQueueTest, TestGenerationNumbers) { int slot; sp<Fence> fence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); sp<GraphicBuffer> buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); @@ -567,7 +569,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr)); + mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Queue the buffer @@ -581,8 +583,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { // always the same one and because async mode gets enabled. int slot; for (int i = 0; i < 5; i++) { - ASSERT_EQ(OK, mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } @@ -619,7 +620,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr)); + mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Queue the buffer @@ -646,8 +647,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { // always return the same one. int slot; for (int i = 0; i < 5; i++) { - ASSERT_EQ(OK, mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } @@ -686,7 +686,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr)); + mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Enable shared buffer mode @@ -703,8 +703,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { // always the same one and because async mode gets enabled. int slot; for (int i = 0; i < 5; i++) { - ASSERT_EQ(OK, mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } @@ -739,8 +738,7 @@ TEST_F(BufferQueueTest, TestTimeouts) { for (int i = 0; i < 5; ++i) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence = Fence::NO_FENCE; - auto result = mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr); + auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr); if (i < 2) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); @@ -767,8 +765,7 @@ TEST_F(BufferQueueTest, TestTimeouts) { for (int i = 0; i < 2; ++i) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence = Fence::NO_FENCE; - ASSERT_EQ(OK, mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, @@ -779,8 +776,7 @@ TEST_F(BufferQueueTest, TestTimeouts) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence = Fence::NO_FENCE; auto startTime = systemTime(); - ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_GE(systemTime() - startTime, TIMEOUT); // We're technically attaching the same buffer multiple times (since we @@ -801,7 +797,7 @@ TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> sourceFence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr)); + mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr, nullptr)); sp<GraphicBuffer> buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->detachBuffer(slot)); @@ -824,7 +820,7 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); sp<GraphicBuffer> firstBuffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer)); @@ -836,7 +832,7 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { // Dequeue a second buffer slot = BufferQueue::INVALID_BUFFER_SLOT; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); sp<GraphicBuffer> secondBuffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer)); @@ -887,8 +883,8 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { int slots[3] = {}; mProducer->setMaxDequeuedBufferCount(3); for (size_t i = 0; i < 3; ++i) { - status_t result = mProducer->dequeueBuffer(&slots[i], &fence, - 0, 0, 0, 0, nullptr); + status_t result = + mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); } @@ -901,8 +897,7 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { // The first segment is a two-buffer segment, so we only put one buffer into // the queue at a time for (size_t i = 0; i < 5; ++i) { - ASSERT_EQ(OK, mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, @@ -917,17 +912,16 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { // two-buffer segment, but then at the end, we put two buffers in the queue // at the same time before draining it. for (size_t i = 0; i < 5; ++i) { - ASSERT_EQ(OK, mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, @@ -942,11 +936,10 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { // The third segment is a triple-buffer segment, so the queue is switching // between one buffer and two buffers deep. - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); for (size_t i = 0; i < 5; ++i) { - ASSERT_EQ(OK, mProducer->dequeueBuffer( - &slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, @@ -1026,8 +1019,8 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { int slots[4] = {}; mProducer->setMaxDequeuedBufferCount(4); for (size_t i = 0; i < 4; ++i) { - status_t result = mProducer->dequeueBuffer(&slots[i], &fence, - 0, 0, 0, 0, nullptr); + status_t result = + mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); } @@ -1038,14 +1031,14 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { // Get buffers in all states: dequeued, filled, acquired, free // Fill 3 buffers - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Dequeue 1 buffer - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); // Acquire and free 1 buffer ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); @@ -1104,8 +1097,8 @@ TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) { int slots[2] = {}; ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2)); for (size_t i = 0; i < 2; ++i) { - status_t result = mProducer->dequeueBuffer(&slots[i], &fence, - 0, 0, 0, 0, nullptr); + status_t result = + mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); } @@ -1115,10 +1108,10 @@ TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) { // Fill 2 buffers without consumer consuming them. Verify that all // queued buffer returns proper bufferReplaced flag - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(false, output.bufferReplaced); - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(true, output.bufferReplaced); } @@ -1140,8 +1133,7 @@ TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); // Dequeue, request, and queue one buffer - status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, - nullptr); + status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); @@ -1156,7 +1148,7 @@ TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // Dequeue and queue the buffer again - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Acquire and release the buffer again. Upon acquiring, the buffer handle @@ -1168,7 +1160,7 @@ TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // Dequeue and queue the buffer again - ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr)); + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Disconnect the producer end. This should clear all of the slots and mark diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index bcfc91c3f5..dd23bd4cb2 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -194,7 +194,8 @@ protected: }; status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) { - return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr); + return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, + nullptr, nullptr); } void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence, @@ -206,9 +207,12 @@ protected: ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH, - DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr))); + + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH, DEFAULT_HEIGHT, + DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, + nullptr, nullptr))); EXPECT_LE(0, *slot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot); @@ -343,11 +347,11 @@ TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) { int dequeuedSlot = -1; sp<Fence> dequeuedFence; - - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, - DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, nullptr))); + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, + DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))); EXPECT_LE(0, dequeuedSlot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot); @@ -403,10 +407,11 @@ TEST_F(IGraphicBufferProducerTest, Queue_ReturnsError) { int dequeuedSlot = -1; sp<Fence> dequeuedFence; - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, - DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, nullptr))); + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, + DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))); // Slot was enqueued without requesting a buffer { @@ -472,10 +477,11 @@ TEST_F(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) { int dequeuedSlot = -1; sp<Fence> dequeuedFence; - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, - DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, nullptr))); + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, + DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))); // No return code, but at least test that it doesn't blow up... // TODO: add a return code @@ -519,12 +525,11 @@ TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { int dequeuedSlot = -1; sp<Fence> dequeuedFence; for (int i = 0; i < maxBuffers; ++i) { - - EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, - DEFAULT_WIDTH, DEFAULT_HEIGHT, - DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, nullptr))) + EXPECT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, + DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))) << "iteration: " << i << ", slot: " << dequeuedSlot; } @@ -557,11 +562,11 @@ TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Fails) { int dequeuedSlot = -1; sp<Fence> dequeuedFence; for (int i = 0; i < 2; i++) { - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, - DEFAULT_WIDTH, DEFAULT_HEIGHT, - DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, nullptr))) + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, + DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))) << "slot: " << dequeuedSlot; } @@ -593,10 +598,11 @@ TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) { // Should now be able to queue/dequeue as many buffers as we want without // blocking for (int i = 0; i < 5; ++i) { - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, - DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, nullptr))) + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, + DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))) << "slot : " << dequeuedSlot; ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer)); ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output)); @@ -610,10 +616,11 @@ TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Fails) { int dequeuedSlot = -1; sp<Fence> dequeuedFence; - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, - DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, nullptr))) + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, + DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))) << "slot: " << dequeuedSlot; } @@ -630,8 +637,9 @@ TEST_F(IGraphicBufferProducerTest, int slot = -1; sp<Fence> fence; - ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, - DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)); + ASSERT_EQ(NO_INIT, + mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)); } TEST_F(IGraphicBufferProducerTest, @@ -649,10 +657,11 @@ TEST_F(IGraphicBufferProducerTest, int slot = -1; sp<Fence> fence; - ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, - DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, - nullptr))); + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + (mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, DEFAULT_HEIGHT, + DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, + nullptr, nullptr))); EXPECT_LE(0, slot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot); diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp index 6bc3ccf53d..bb6b8a59fe 100644 --- a/libs/gui/tests/Malicious.cpp +++ b/libs/gui/tests/Malicious.cpp @@ -38,8 +38,10 @@ public: } status_t setAsyncMode(bool async) override { return mProducer->setAsyncMode(async); } status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, PixelFormat format, - uint64_t usage, FrameEventHistoryDelta* outTimestamps) override { - return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outTimestamps); + uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) override { + return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, + outTimestamps); } status_t detachBuffer(int slot) override { return mProducer->detachBuffer(slot); } status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override { @@ -90,6 +92,9 @@ public: } void getFrameTimestamps(FrameEventHistoryDelta*) override {} status_t getUniqueId(uint64_t* outId) const override { return mProducer->getUniqueId(outId); } + status_t getConsumerUsage(uint64_t* outUsage) const override { + return mProducer->getConsumerUsage(outUsage); + } protected: sp<IGraphicBufferProducer> mProducer; @@ -105,10 +110,10 @@ public: // Override dequeueBuffer, optionally corrupting the returned slot number status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height, - PixelFormat format, uint64_t usage, + PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) override { EXPECT_EQ(BUFFER_NEEDS_REALLOCATION, - mProducer->dequeueBuffer(buf, fence, width, height, format, usage, + mProducer->dequeueBuffer(buf, fence, width, height, format, usage, outBufferAge, outTimestamps)); EXPECT_EQ(mExpectedSlot, *buf); if (mMaliciousValue != 0) { diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp index e2f494898e..ad6e051684 100644 --- a/libs/gui/tests/StreamSplitter_test.cpp +++ b/libs/gui/tests/StreamSplitter_test.cpp @@ -82,8 +82,8 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; @@ -116,8 +116,8 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { // This should succeed even with allocation disabled since it will have // received the buffer back from the output BufferQueue ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); } TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { @@ -154,8 +154,8 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; @@ -191,8 +191,8 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { // This should succeed even with allocation disabled since it will have // received the buffer back from the output BufferQueues ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); } TEST_F(StreamSplitterTest, OutputAbandonment) { @@ -218,8 +218,8 @@ TEST_F(StreamSplitterTest, OutputAbandonment) { sp<Fence> fence; sp<GraphicBuffer> buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); // Abandon the output @@ -231,8 +231,9 @@ TEST_F(StreamSplitterTest, OutputAbandonment) { ASSERT_EQ(OK, inputProducer->queueBuffer(slot, qbInput, &qbOutput)); // Input should be abandoned - ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, - GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr)); + ASSERT_EQ(NO_INIT, + inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr)); } } // namespace android diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index e572e51b2c..ca43c68f92 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -539,6 +539,9 @@ public: return NO_ERROR; } status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; } + status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override { + return NO_ERROR; + } protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/hwc2on1adapter/HWC2On1Adapter.cpp b/libs/hwc2on1adapter/HWC2On1Adapter.cpp index 8c6ef691a2..77f06bbbe7 100644 --- a/libs/hwc2on1adapter/HWC2On1Adapter.cpp +++ b/libs/hwc2on1adapter/HWC2On1Adapter.cpp @@ -426,7 +426,13 @@ Error HWC2On1Adapter::registerCallback(Callback descriptor, std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); - mCallbacks[descriptor] = {callbackData, pointer}; + if (pointer != nullptr) { + mCallbacks[descriptor] = {callbackData, pointer}; + } else { + ALOGI("unregisterCallback(%s)", to_string(descriptor).c_str()); + mCallbacks.erase(descriptor); + return Error::None; + } bool hasPendingInvalidate = false; std::vector<hwc2_display_t> displayIds; @@ -2005,10 +2011,21 @@ Error HWC2On1Adapter::Layer::setTransform(Transform transform) { return Error::None; } +static bool compareRects(const hwc_rect_t& rect1, const hwc_rect_t& rect2) { + return rect1.left == rect2.left && + rect1.right == rect2.right && + rect1.top == rect2.top && + rect1.bottom == rect2.bottom; +} + Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) { - mVisibleRegion.resize(visible.numRects); - std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin()); - mDisplay.markGeometryChanged(); + if ((getNumVisibleRegions() != visible.numRects) || + !std::equal(mVisibleRegion.begin(), mVisibleRegion.end(), visible.rects, + compareRects)) { + mVisibleRegion.resize(visible.numRects); + std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin()); + mDisplay.markGeometryChanged(); + } return Error::None; } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 92944191c7..2f399765a0 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -34,6 +34,7 @@ cc_library { clang: true, shared_libs: [ + "libbase", "liblog", "libcutils", ], diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 63e98bb540..9abd04ca04 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -496,7 +496,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; - updateTouchState(&mMsg); + updateTouchState(mMsg); initializeMotionEvent(motionEvent, &mMsg); *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; @@ -564,7 +564,7 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, uint32_t chain = 0; for (size_t i = 0; i < count; i++) { InputMessage& msg = batch.samples.editItemAt(i); - updateTouchState(&msg); + updateTouchState(msg); if (i) { SeqChain seqChain; seqChain.seq = msg.body.motion.seq; @@ -584,20 +584,19 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, return OK; } -void InputConsumer::updateTouchState(InputMessage* msg) { +void InputConsumer::updateTouchState(InputMessage& msg) { if (!mResampleTouch || - !(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { + !(msg.body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { return; } - int32_t deviceId = msg->body.motion.deviceId; - int32_t source = msg->body.motion.source; - nsecs_t eventTime = msg->body.motion.eventTime; + int32_t deviceId = msg.body.motion.deviceId; + int32_t source = msg.body.motion.source; // Update the touch state history to incorporate the new input message. // If the message is in the past relative to the most recently produced resampled // touch, then use the resampled time and coordinates instead. - switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) { + switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: { ssize_t index = findTouchState(deviceId, source); if (index < 0) { @@ -615,9 +614,8 @@ void InputConsumer::updateTouchState(InputMessage* msg) { if (index >= 0) { TouchState& touchState = mTouchStates.editItemAt(index); touchState.addHistory(msg); - if (eventTime < touchState.lastResample.eventTime) { - rewriteMessage(touchState, msg); - } else { + bool messageRewritten = rewriteMessage(touchState, msg); + if (!messageRewritten) { touchState.lastResample.idBits.clear(); } } @@ -628,7 +626,7 @@ void InputConsumer::updateTouchState(InputMessage* msg) { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { TouchState& touchState = mTouchStates.editItemAt(index); - touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); + touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); rewriteMessage(touchState, msg); } break; @@ -639,7 +637,7 @@ void InputConsumer::updateTouchState(InputMessage* msg) { if (index >= 0) { TouchState& touchState = mTouchStates.editItemAt(index); rewriteMessage(touchState, msg); - touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); + touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); } break; } @@ -666,23 +664,28 @@ void InputConsumer::updateTouchState(InputMessage* msg) { } } -void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) { - for (uint32_t i = 0; i < msg->body.motion.pointerCount; i++) { - uint32_t id = msg->body.motion.pointers[i].properties.id; +bool InputConsumer::rewriteMessage(const TouchState& state, InputMessage& msg) { + bool messageRewritten = false; + nsecs_t eventTime = msg.body.motion.eventTime; + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + uint32_t id = msg.body.motion.pointers[i].properties.id; if (state.lastResample.idBits.hasBit(id)) { - PointerCoords& msgCoords = msg->body.motion.pointers[i].coords; + PointerCoords& msgCoords = msg.body.motion.pointers[i].coords; const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); + if (eventTime < state.lastResample.eventTime || + state.recentCoordinatesAreIdentical(id)) { + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); #if DEBUG_RESAMPLING - ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, - resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X), - resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y), - msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X), - msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y)); + ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, + resampleCoords.getX(), resampleCoords.getY(), + msgCoords.getX(), msgCoords.getY()); #endif - msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); - msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); + messageRewritten = true; + } } } + return messageRewritten; } void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, @@ -710,6 +713,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, } // Ensure that the current sample has all of the pointers that need to be reported. + // Also ensure that the past two "real" touch events do not contain duplicate coordinates const History* current = touchState.getHistory(0); size_t pointerCount = event->getPointerCount(); for (size_t i = 0; i < pointerCount; i++) { @@ -720,6 +724,12 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, #endif return; } + if (touchState.recentCoordinatesAreIdentical(id)) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, past two historical events have duplicate coordinates"); +#endif + return; + } } // Find the data to use for resampling. @@ -729,12 +739,12 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, if (next) { // Interpolate between current sample and future sample. // So current->eventTime <= sampleTime <= future.eventTime. - future.initializeFrom(next); + future.initializeFrom(*next); other = &future; nsecs_t delta = future.eventTime - current->eventTime; if (delta < RESAMPLE_MIN_DELTA) { #if DEBUG_RESAMPLING - ALOGD("Not resampled, delta time is too small: %lld ns.", delta); + ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta); #endif return; } @@ -746,12 +756,12 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, nsecs_t delta = current->eventTime - other->eventTime; if (delta < RESAMPLE_MIN_DELTA) { #if DEBUG_RESAMPLING - ALOGD("Not resampled, delta time is too small: %lld ns.", delta); + ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta); #endif return; } else if (delta > RESAMPLE_MAX_DELTA) { #if DEBUG_RESAMPLING - ALOGD("Not resampled, delta time is too large: %lld ns.", delta); + ALOGD("Not resampled, delta time is too large: %" PRId64 " ns.", delta); #endif return; } @@ -759,7 +769,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, if (sampleTime > maxPredict) { #if DEBUG_RESAMPLING ALOGD("Sample time is too far in the future, adjusting prediction " - "from %lld to %lld ns.", + "from %" PRId64 " to %" PRId64 " ns.", sampleTime - current->eventTime, maxPredict - current->eventTime); #endif sampleTime = maxPredict; diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 7f6b1576cf..62acea360e 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -23,13 +23,14 @@ // Log debug messages about the progress of the algorithm itself. #define DEBUG_STRATEGY 0 -#include <math.h> +#include <inttypes.h> #include <limits.h> +#include <math.h> +#include <android-base/stringprintf.h> #include <cutils/properties.h> #include <input/VelocityTracker.h> #include <utils/BitSet.h> -#include <utils/String8.h> #include <utils/Timers.h> namespace android { @@ -46,8 +47,7 @@ static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS; static float vectorDot(const float* a, const float* b, uint32_t m) { float r = 0; - while (m) { - m--; + for (size_t i = 0; i < m; i++) { r += *(a++) * *(b++); } return r; @@ -55,8 +55,7 @@ static float vectorDot(const float* a, const float* b, uint32_t m) { static float vectorNorm(const float* a, uint32_t m) { float r = 0; - while (m) { - m--; + for (size_t i = 0; i < m; i++) { float t = *(a++); r += t * t; } @@ -64,36 +63,36 @@ static float vectorNorm(const float* a, uint32_t m) { } #if DEBUG_STRATEGY || DEBUG_VELOCITY -static String8 vectorToString(const float* a, uint32_t m) { - String8 str; - str.append("["); - while (m--) { - str.appendFormat(" %f", *(a++)); - if (m) { - str.append(","); +static std::string vectorToString(const float* a, uint32_t m) { + std::string str; + str += "["; + for (size_t i = 0; i < m; i++) { + if (i) { + str += ","; } + str += android::base::StringPrintf(" %f", *(a++)); } - str.append(" ]"); + str += " ]"; return str; } -static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) { - String8 str; - str.append("["); +static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) { + std::string str; + str = "["; for (size_t i = 0; i < m; i++) { if (i) { - str.append(","); + str += ","; } - str.append(" ["); + str += " ["; for (size_t j = 0; j < n; j++) { if (j) { - str.append(","); + str += ","; } - str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]); + str += android::base::StringPrintf(" %f", a[rowMajor ? i * n + j : j * m + i]); } - str.append(" ]"); + str += " ]"; } - str.append(" ]"); + str += " ]"; return str; } #endif @@ -244,7 +243,7 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Posi mStrategy->addMovement(eventTime, idBits, positions); #if DEBUG_VELOCITY - ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d", + ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d", eventTime, idBits.value, mActivePointerId); for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) { uint32_t id = iterBits.firstMarkedBit(); @@ -256,8 +255,8 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Posi "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)", id, positions[index].x, positions[index].y, int(estimator.degree), - vectorToString(estimator.xCoeff, estimator.degree + 1).string(), - vectorToString(estimator.yCoeff, estimator.degree + 1).string(), + vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(), + vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(), estimator.confidence); } #endif @@ -443,8 +442,8 @@ static bool solveLeastSquares(const float* x, const float* y, const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) { #if DEBUG_STRATEGY ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), - vectorToString(x, m).string(), vectorToString(y, m).string(), - vectorToString(w, m).string()); + vectorToString(x, m).c_str(), vectorToString(y, m).c_str(), + vectorToString(w, m).c_str()); #endif // Expand the X vector to a matrix A, pre-multiplied by the weights. @@ -456,7 +455,7 @@ static bool solveLeastSquares(const float* x, const float* y, } } #if DEBUG_STRATEGY - ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string()); + ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str()); #endif // Apply the Gram-Schmidt process to A to obtain its QR decomposition. @@ -491,8 +490,8 @@ static bool solveLeastSquares(const float* x, const float* y, } } #if DEBUG_STRATEGY - ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string()); - ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string()); + ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str()); + ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str()); // calculate QR, if we factored A correctly then QR should equal A float qr[n][m]; @@ -504,7 +503,7 @@ static bool solveLeastSquares(const float* x, const float* y, } } } - ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string()); + ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str()); #endif // Solve R B = Qt W Y to find B. This is easy because R is upper triangular. @@ -522,7 +521,7 @@ static bool solveLeastSquares(const float* x, const float* y, outB[i] /= r[i][i]; } #if DEBUG_STRATEGY - ALOGD(" - b=%s", vectorToString(outB, n).string()); + ALOGD(" - b=%s", vectorToString(outB, n).c_str()); #endif // Calculate the coefficient of determination as 1 - (SSerr / SStot) where @@ -557,6 +556,46 @@ static bool solveLeastSquares(const float* x, const float* y, return true; } +/* + * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to + * the default implementation + */ +static float solveUnweightedLeastSquaresDeg2(const float* x, const float* y, size_t count) { + float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0; + + for (size_t i = 0; i < count; i++) { + float xi = x[i]; + float yi = y[i]; + float xi2 = xi*xi; + float xi3 = xi2*xi; + float xi4 = xi3*xi; + float xi2yi = xi2*yi; + float xiyi = xi*yi; + + sxi += xi; + sxi2 += xi2; + sxiyi += xiyi; + sxi2yi += xi2yi; + syi += yi; + sxi3 += xi3; + sxi4 += xi4; + } + + float Sxx = sxi2 - sxi*sxi / count; + float Sxy = sxiyi - sxi*syi / count; + float Sxx2 = sxi3 - sxi*sxi2 / count; + float Sx2y = sxi2yi - sxi2*syi / count; + float Sx2x2 = sxi4 - sxi2*sxi2 / count; + + float numerator = Sxy*Sx2x2 - Sx2y*Sxx2; + float denominator = Sxx*Sx2x2 - Sxx2*Sxx2; + if (denominator == 0) { + ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2); + return 0; + } + return numerator/denominator; +} + bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const { outEstimator->clear(); @@ -598,6 +637,19 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, degree = m - 1; } if (degree >= 1) { + if (degree == 2 && mWeighting == WEIGHTING_NONE) { // optimize unweighted, degree=2 fit + outEstimator->time = newestMovement.eventTime; + outEstimator->degree = 2; + outEstimator->confidence = 1; + outEstimator->xCoeff[0] = 0; // only slope is calculated, set rest of coefficients = 0 + outEstimator->yCoeff[0] = 0; + outEstimator->xCoeff[1] = solveUnweightedLeastSquaresDeg2(time, x, m); + outEstimator->yCoeff[1] = solveUnweightedLeastSquaresDeg2(time, y, m); + outEstimator->xCoeff[2] = 0; + outEstimator->yCoeff[2] = 0; + return true; + } + float xdet, ydet; uint32_t n = degree + 1; if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) @@ -608,8 +660,8 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, #if DEBUG_STRATEGY ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f", int(outEstimator->degree), - vectorToString(outEstimator->xCoeff, n).string(), - vectorToString(outEstimator->yCoeff, n).string(), + vectorToString(outEstimator->xCoeff, n).c_str(), + vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence); #endif return true; diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 3df97a1b4a..64908049d6 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -115,7 +115,7 @@ enum { * The consumer gralloc usage bits currently set by the consumer. * The values are defined in hardware/libhardware/include/gralloc.h. */ - NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10, + NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10, /* deprecated */ /** * Transformation that will by applied to buffers by the hwcomposer. @@ -224,6 +224,7 @@ enum { NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28, NATIVE_WINDOW_GET_HDR_SUPPORT = 29, NATIVE_WINDOW_SET_USAGE64 = 30, + NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31, // clang-format on }; @@ -900,13 +901,18 @@ static inline int native_window_get_frame_timestamps( static inline int native_window_get_wide_color_support( struct ANativeWindow* window, bool* outSupport) { - return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT, - outSupport); + return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT, + outSupport); } static inline int native_window_get_hdr_support(struct ANativeWindow* window, bool* outSupport) { - return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport); + return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport); +} + +static inline int native_window_get_consumer_usage(struct ANativeWindow* window, + uint64_t* outUsage) { + return window->perform(window, NATIVE_WINDOW_GET_CONSUMER_USAGE64, outUsage); } __END_DECLS diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS deleted file mode 100644 index 6a38a1ff14..0000000000 --- a/libs/sensor/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -ashutoshj@google.com -pengxu@google.com diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index e47d2b894b..07aba321a8 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -111,6 +111,12 @@ cc_library_shared { ], } +cc_library_headers { + name: "libui_headers", + export_include_dirs: ["include"], + vendor_available: true, +} + subdirs = [ "tests", "tools", diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index d5676cc2b8..2d72944665 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -16,10 +16,13 @@ #include <ui/DebugUtils.h> #include <ui/PixelFormat.h> +#include <ui/Rect.h> #include <android-base/stringprintf.h> #include <string> +using android::base::StringPrintf; + std::string decodeStandard(android_dataspace dataspace) { const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK); switch (dataspaceSelect) { @@ -187,7 +190,7 @@ std::string decodeRange(android_dataspace dataspace) { std::string dataspaceDetails(android_dataspace dataspace) { if (dataspace == 0) { - return "Default (0)"; + return "Default"; } return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(), decodeTransfer(dataspace).c_str(), @@ -262,3 +265,7 @@ std::string decodePixelFormat(android::PixelFormat format) { return android::base::StringPrintf("Unknown %#08x", format); } } + +std::string to_string(const android::Rect& rect) { + return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom); +} diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp index 39adc5e929..755e60c82e 100644 --- a/libs/ui/HdrCapabilities.cpp +++ b/libs/ui/HdrCapabilities.cpp @@ -76,7 +76,7 @@ status_t HdrCapabilities::unflatten(void const* buffer, size_t size) { mMaxAverageLuminance = reinterpret_cast<float const&>(buf[1]); mMinLuminance = reinterpret_cast<float const&>(buf[2]); if (itemCount) { - mSupportedHdrTypes.reserve(itemCount); + mSupportedHdrTypes.resize(itemCount); for (size_t i = 0; i < itemCount; ++i) { mSupportedHdrTypes[i] = buf[4 + i]; } diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h index 30f4a59fe0..dad9446b3a 100644 --- a/libs/ui/include/ui/DebugUtils.h +++ b/libs/ui/include/ui/DebugUtils.h @@ -21,9 +21,14 @@ #include <string> +namespace android { +class Rect; +} + std::string decodeStandard(android_dataspace dataspace); std::string decodeTransfer(android_dataspace dataspace); std::string decodeRange(android_dataspace dataspace); std::string dataspaceDetails(android_dataspace dataspace); std::string decodeColorMode(android_color_mode colormode); std::string decodePixelFormat(android::PixelFormat format); +std::string to_string(const android::Rect& rect); diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index 016de647c2..a2287e1127 100644 --- a/libs/vr/libbufferhub/Android.bp +++ b/libs/vr/libbufferhub/Android.bp @@ -37,7 +37,8 @@ sharedLibraries = [ "libnativewindow" ] -HeaderLibraries = [ +headerLibraries = [ + "libdvr_headers", "libnativebase_headers", ] @@ -53,7 +54,7 @@ cc_library { export_include_dirs: localIncludeFiles, static_libs: staticLibraries, shared_libs: sharedLibraries, - header_libs: HeaderLibraries, + header_libs: headerLibraries, name: "libbufferhub", export_header_lib_headers: [ "libnativebase_headers", @@ -65,6 +66,7 @@ cc_test { srcs: ["bufferhub_tests.cpp"], static_libs: ["libbufferhub"] + staticLibraries, shared_libs: sharedLibraries, + header_libs: headerLibraries, name: "bufferhub_tests", } diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp index b9a53b0ced..97341b1477 100644 --- a/libs/vr/libbufferhub/buffer_hub_client.cpp +++ b/libs/vr/libbufferhub/buffer_hub_client.cpp @@ -2,7 +2,7 @@ #include <log/log.h> #include <poll.h> -#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <sys/epoll.h> #include <utils/Trace.h> #include <mutex> @@ -12,9 +12,8 @@ #include "include/private/dvr/bufferhub_rpc.h" -using android::pdx::LocalHandle; using android::pdx::LocalChannelHandle; -using android::pdx::rpc::WrapBuffer; +using android::pdx::LocalHandle; using android::pdx::Status; namespace android { @@ -29,7 +28,11 @@ BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path) endpoint_path)}, id_(-1) {} -BufferHubBuffer::~BufferHubBuffer() {} +BufferHubBuffer::~BufferHubBuffer() { + if (metadata_header_ != nullptr) { + metadata_buffer_.Unlock(); + } +} Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() { Status<LocalChannelHandle> status = @@ -43,7 +46,7 @@ Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() { int BufferHubBuffer::ImportBuffer() { ATRACE_NAME("BufferHubBuffer::ImportBuffer"); - Status<NativeBufferHandle<LocalHandle>> status = + Status<BufferDescription<LocalHandle>> status = InvokeRemoteMethod<BufferHubRPC::GetBuffer>(); if (!status) { ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s", @@ -54,24 +57,135 @@ int BufferHubBuffer::ImportBuffer() { return -EIO; } - auto buffer_handle = status.take(); + auto buffer_desc = status.take(); // Stash the buffer id to replace the value in id_. - const int new_id = buffer_handle.id(); + const int new_id = buffer_desc.id(); // Import the buffer. IonBuffer ion_buffer; - ALOGD_IF( - TRACE, "BufferHubBuffer::ImportBuffer: id=%d FdCount=%zu IntCount=%zu", - buffer_handle.id(), buffer_handle.FdCount(), buffer_handle.IntCount()); + ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d.", buffer_desc.id()); - const int ret = buffer_handle.Import(&ion_buffer); - if (ret < 0) + if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) return ret; - // If the import succeeds, replace the previous buffer and id. + // Import the metadata. + IonBuffer metadata_buffer; + if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) { + ALOGE("Failed to import metadata buffer, error=%d", ret); + return ret; + } + size_t metadata_buf_size = metadata_buffer.width(); + if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) { + ALOGE("BufferHubBuffer::ImportBuffer: metadata buffer too small: %zu", + metadata_buf_size); + return -ENOMEM; + } + + // If all imports succee, replace the previous buffer and id. buffer_ = std::move(ion_buffer); + metadata_buffer_ = std::move(metadata_buffer); + metadata_buf_size_ = metadata_buf_size; + user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize; + + void* metadata_ptr = nullptr; + if (const int ret = + metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0, + /*y=*/0, metadata_buf_size_, + /*height=*/1, &metadata_ptr)) { + ALOGE("BufferHubBuffer::ImportBuffer: Failed to lock metadata."); + return ret; + } + + // Set up shared fences. + shared_acquire_fence_ = buffer_desc.take_acquire_fence(); + shared_release_fence_ = buffer_desc.take_release_fence(); + if (!shared_acquire_fence_ || !shared_release_fence_) { + ALOGE("BufferHubBuffer::ImportBuffer: Failed to import shared fences."); + return -EIO; + } + + metadata_header_ = + reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr); + if (user_metadata_size_) { + user_metadata_ptr_ = + reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) + + BufferHubDefs::kMetadataHeaderSize); + } else { + user_metadata_ptr_ = nullptr; + } + id_ = new_id; + buffer_state_bit_ = buffer_desc.buffer_state_bit(); + + // Note that here the buffer state is mapped from shared memory as an atomic + // object. The std::atomic's constructor will not be called so that the + // original value stored in the memory region will be preserved. + buffer_state_ = &metadata_header_->buffer_state; + ALOGD_IF(TRACE, + "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".", + id(), buffer_state_->load()); + fence_state_ = &metadata_header_->fence_state; + ALOGD_IF(TRACE, + "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".", + id(), fence_state_->load()); + + return 0; +} + +inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const { + if (user_metadata_size && !user_metadata_ptr_) { + ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata."); + return -EINVAL; + } + if (user_metadata_size > user_metadata_size_) { + ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.", + user_metadata_size, user_metadata_size_); + return -E2BIG; + } + return 0; +} + +int BufferHubBuffer::UpdateSharedFence(const LocalHandle& new_fence, + const LocalHandle& shared_fence) { + if (pending_fence_fd_.Get() != new_fence.Get()) { + // First, replace the old fd if there was already one. Skipping if the new + // one is the same as the old. + if (pending_fence_fd_.IsValid()) { + const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL, + pending_fence_fd_.Get(), nullptr); + ALOGW_IF(ret, + "BufferHubBuffer::UpdateSharedFence: failed to remove old fence " + "fd from epoll set, error: %s.", + strerror(errno)); + } + + if (new_fence.IsValid()) { + // If ready fence is valid, we put that into the epoll set. + epoll_event event; + event.events = EPOLLIN; + event.data.u64 = buffer_state_bit(); + pending_fence_fd_ = new_fence.Duplicate(); + if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(), + &event) < 0) { + const int error = errno; + ALOGE( + "BufferHubBuffer::UpdateSharedFence: failed to add new fence fd " + "into epoll set, error: %s.", + strerror(error)); + return -error; + } + // Set bit in fence state to indicate that there is a fence from this + // producer or consumer. + fence_state_->fetch_or(buffer_state_bit()); + } else { + // Unset bit in fence state to indicate that there is no fence, so that + // when consumer to acquire or producer to acquire, it knows no need to + // check fence for this buffer. + fence_state_->fetch_and(~buffer_state_bit()); + } + } + return 0; } @@ -131,31 +245,144 @@ std::unique_ptr<BufferConsumer> BufferConsumer::Import( : LocalChannelHandle{nullptr, -status.error()}); } +int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence) { + if (!out_meta) + return -EINVAL; + + // Only check producer bit and this consumer buffer's particular consumer bit. + // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit + // is not set. + uint64_t buffer_state = buffer_state_->load(); + if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) { + ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64 + " buffer_state_bit=%" PRIx64 ".", + id(), buffer_state, buffer_state_bit()); + return -EBUSY; + } + + // Copy the canonical metadata. + void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); + memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata)); + // Fill in the user_metadata_ptr in address space of the local process. + if (out_meta->user_metadata_size) { + out_meta->user_metadata_ptr = + reinterpret_cast<uint64_t>(user_metadata_ptr_); + } else { + out_meta->user_metadata_ptr = 0; + } + + uint64_t fence_state = fence_state_->load(); + // If there is an acquire fence from producer, we need to return it. + if (fence_state & BufferHubDefs::kProducerStateBit) { + *out_fence = shared_acquire_fence_.Duplicate(); + } + + // Set the consumer bit unique to this consumer. + BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit()); + return 0; +} + int BufferConsumer::Acquire(LocalHandle* ready_fence) { return Acquire(ready_fence, nullptr, 0); } int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta, - size_t meta_size_bytes) { + size_t user_metadata_size) { ATRACE_NAME("BufferConsumer::Acquire"); - LocalFence fence; - auto return_value = - std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes)); - auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>( - &return_value, meta_size_bytes); - if (status && ready_fence) - *ready_fence = fence.take(); - return status ? 0 : -status.error(); + + if (const int error = CheckMetadata(user_metadata_size)) + return error; + + DvrNativeBufferMetadata canonical_meta; + if (const int error = LocalAcquire(&canonical_meta, ready_fence)) + return error; + + if (meta && user_metadata_size) { + void* metadata_src = + reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); + if (metadata_src) { + memcpy(meta, metadata_src, user_metadata_size); + } else { + ALOGW("BufferConsumer::Acquire: no user-defined metadata."); + } + } + + auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>(); + if (!status) + return -status.error(); + return 0; +} + +int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence) { + ATRACE_NAME("BufferConsumer::AcquireAsync"); + + if (const int error = LocalAcquire(out_meta, out_fence)) + return error; + + auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode); + if (!status) + return -status.error(); + return 0; +} + +int BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence) { + if (const int error = CheckMetadata(meta->user_metadata_size)) + return error; + + // Check invalid state transition. + uint64_t buffer_state = buffer_state_->load(); + if (!BufferHubDefs::IsBufferAcquired(buffer_state)) { + ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".", + id(), buffer_state); + return -EBUSY; + } + + // On release, only the user requested metadata is copied back into the shared + // memory for metadata. Since there are multiple consumers, it doesn't make + // sense to send the canonical metadata back to the producer. However, one of + // the consumer can still choose to write up to user_metadata_size bytes of + // data into user_metadata_ptr. + if (meta->user_metadata_ptr && meta->user_metadata_size) { + void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); + memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); + } + + // Send out the release fence through the shared epoll fd. Note that during + // releasing the producer is not expected to be polling on the fence. + if (const int error = UpdateSharedFence(release_fence, shared_release_fence_)) + return error; + + // For release operation, the client don't need to change the state as it's + // bufferhubd's job to flip the produer bit once all consumers are released. + return 0; } int BufferConsumer::Release(const LocalHandle& release_fence) { ATRACE_NAME("BufferConsumer::Release"); + + DvrNativeBufferMetadata meta; + if (const int error = LocalRelease(&meta, release_fence)) + return error; + return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>( BorrowedFence(release_fence.Borrow()))); } int BufferConsumer::ReleaseAsync() { + DvrNativeBufferMetadata meta; + return ReleaseAsync(&meta, LocalHandle()); +} + +int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence) { ATRACE_NAME("BufferConsumer::ReleaseAsync"); + + if (const int error = LocalRelease(meta, release_fence)) + return error; + return ReturnStatusOrError( SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); } @@ -168,24 +395,25 @@ int BufferConsumer::SetIgnore(bool ignore) { } BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, - uint32_t usage, size_t metadata_size) - : BufferProducer(width, height, format, usage, usage, metadata_size) {} + uint32_t usage, size_t user_metadata_size) + : BufferProducer(width, height, format, usage, usage, user_metadata_size) {} BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, uint64_t producer_usage, uint64_t consumer_usage, - size_t metadata_size) + size_t user_metadata_size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u " "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 - " metadata_size=%zu", + " user_metadata_size=%zu", event_fd(), width, height, format, producer_usage, consumer_usage, - metadata_size); + user_metadata_size); // (b/37881101) Deprecate producer/consumer usage auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( - width, height, format, (producer_usage | consumer_usage), metadata_size); + width, height, format, (producer_usage | consumer_usage), + user_metadata_size); if (!status) { ALOGE( "BufferProducer::BufferProducer: Failed to create producer buffer: %s", @@ -206,27 +434,28 @@ BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, BufferProducer::BufferProducer(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, uint32_t usage, - size_t meta_size_bytes) + size_t user_metadata_size) : BufferProducer(name, user_id, group_id, width, height, format, usage, - usage, meta_size_bytes) {} + usage, user_metadata_size) {} BufferProducer::BufferProducer(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, uint64_t producer_usage, - uint64_t consumer_usage, size_t meta_size_bytes) + uint64_t consumer_usage, + size_t user_metadata_size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d " "group_id=%d width=%u height=%u format=%u producer_usage=%" PRIx64 - " consumer_usage=%" PRIx64 " meta_size_bytes=%zu", + " consumer_usage=%" PRIx64 " user_metadata_size=%zu", event_fd(), name.c_str(), user_id, group_id, width, height, format, - producer_usage, consumer_usage, meta_size_bytes); + producer_usage, consumer_usage, user_metadata_size); // (b/37881101) Deprecate producer/consumer usage auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>( name, user_id, group_id, width, height, format, - (producer_usage | consumer_usage), meta_size_bytes); + (producer_usage | consumer_usage), user_metadata_size); if (!status) { ALOGE( "BufferProducer::BufferProducer: Failed to create/get persistent " @@ -260,12 +489,12 @@ BufferProducer::BufferProducer(uint64_t producer_usage, uint64_t consumer_usage, const int width = static_cast<int>(size); const int height = 1; const int format = HAL_PIXEL_FORMAT_BLOB; - const size_t meta_size_bytes = 0; + const size_t user_metadata_size = 0; // (b/37881101) Deprecate producer/consumer usage auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( width, height, format, (producer_usage | consumer_usage), - meta_size_bytes); + user_metadata_size); if (!status) { ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s", status.GetErrorMessage().c_str()); @@ -299,12 +528,12 @@ BufferProducer::BufferProducer(const std::string& name, int user_id, const int width = static_cast<int>(size); const int height = 1; const int format = HAL_PIXEL_FORMAT_BLOB; - const size_t meta_size_bytes = 0; + const size_t user_metadata_size = 0; // (b/37881101) Deprecate producer/consumer usage auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>( name, user_id, group_id, width, height, format, - (producer_usage | consumer_usage), meta_size_bytes); + (producer_usage | consumer_usage), user_metadata_size); if (!status) { ALOGE( "BufferProducer::BufferProducer: Failed to create persistent " @@ -360,28 +589,141 @@ BufferProducer::BufferProducer(LocalChannelHandle channel) } } +int BufferProducer::LocalPost(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence) { + if (const int error = CheckMetadata(meta->user_metadata_size)) + return error; + + // Check invalid state transition. + uint64_t buffer_state = buffer_state_->load(); + if (!BufferHubDefs::IsBufferGained(buffer_state)) { + ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".", + id(), buffer_state); + return -EBUSY; + } + + // Copy the canonical metadata. + void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); + memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata)); + // Copy extra user requested metadata. + if (meta->user_metadata_ptr && meta->user_metadata_size) { + void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); + memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); + } + + // Send out the acquire fence through the shared epoll fd. Note that during + // posting no consumer is not expected to be polling on the fence. + if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_)) + return error; + + // Set the producer bit atomically to transit into posted state. + BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, + BufferHubDefs::kProducerStateBit); + return 0; +} + int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta, - size_t meta_size_bytes) { + size_t user_metadata_size) { ATRACE_NAME("BufferProducer::Post"); + + // Populate cononical metadata for posting. + DvrNativeBufferMetadata canonical_meta; + canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta); + canonical_meta.user_metadata_size = user_metadata_size; + + if (const int error = LocalPost(&canonical_meta, ready_fence)) + return error; + return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>( - BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes))); + BorrowedFence(ready_fence.Borrow()))); +} + +int BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence) { + ATRACE_NAME("BufferProducer::PostAsync"); + + if (const int error = LocalPost(meta, ready_fence)) + return error; + + return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode)); +} + +int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence) { + uint64_t buffer_state = buffer_state_->load(); + ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".", + id(), buffer_state); + + if (!out_meta) + return -EINVAL; + + if (!BufferHubDefs::IsBufferReleased(buffer_state)) { + if (BufferHubDefs::IsBufferGained(buffer_state)) { + // We don't want to log error when gaining a newly allocated + // buffer. + ALOGI("BufferProducer::LocalGain: already gained id=%d.", id()); + return -EALREADY; + } + ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".", + id(), buffer_state); + return -EBUSY; + } + + // Canonical metadata is undefined on Gain. Except for user_metadata and + // release_fence_mask. Fill in the user_metadata_ptr in address space of the + // local process. + if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) { + out_meta->user_metadata_size = + metadata_header_->metadata.user_metadata_size; + out_meta->user_metadata_ptr = + reinterpret_cast<uint64_t>(user_metadata_ptr_); + } else { + out_meta->user_metadata_size = 0; + out_meta->user_metadata_ptr = 0; + } + + uint64_t fence_state = fence_state_->load(); + // If there is an release fence from consumer, we need to return it. + if (fence_state & BufferHubDefs::kConsumerStateMask) { + *out_fence = shared_release_fence_.Duplicate(); + out_meta->release_fence_mask = + fence_state & BufferHubDefs::kConsumerStateMask; + } + + // Clear out all bits and the buffer is now back to gained state. + buffer_state_->store(0ULL); + return 0; } int BufferProducer::Gain(LocalHandle* release_fence) { ATRACE_NAME("BufferProducer::Gain"); + + DvrNativeBufferMetadata meta; + if (const int error = LocalGain(&meta, release_fence)) + return error; + auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>(); if (!status) return -status.error(); - if (release_fence) - *release_fence = status.take().take(); return 0; } -int BufferProducer::GainAsync() { +int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta, + LocalHandle* release_fence) { ATRACE_NAME("BufferProducer::GainAsync"); + + if (const int error = LocalGain(out_meta, release_fence)) + return error; + return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); } +int BufferProducer::GainAsync() { + DvrNativeBufferMetadata meta; + LocalHandle fence; + return GainAsync(&meta, &fence); +} + std::unique_ptr<BufferProducer> BufferProducer::Import( LocalChannelHandle channel) { ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value()); diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp index 1daa5d62d7..c4b9a8c88d 100644 --- a/libs/vr/libbufferhub/bufferhub_tests.cpp +++ b/libs/vr/libbufferhub/bufferhub_tests.cpp @@ -1,5 +1,9 @@ #include <gtest/gtest.h> +#include <poll.h> #include <private/dvr/buffer_hub_client.h> +#include <private/dvr/bufferhub_rpc.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> #include <mutex> #include <thread> @@ -13,8 +17,10 @@ return result; \ })() -using android::dvr::BufferProducer; using android::dvr::BufferConsumer; +using android::dvr::BufferHubDefs::kConsumerStateMask; +using android::dvr::BufferHubDefs::kProducerStateBit; +using android::dvr::BufferProducer; using android::pdx::LocalHandle; const int kWidth = 640; @@ -37,29 +43,149 @@ TEST_F(LibBufferHubTest, TestBasicUsage) { BufferConsumer::Import(c->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); + // Producer state mask is unique, i.e. 1. + EXPECT_EQ(p->buffer_state_bit(), kProducerStateBit); + // Consumer state mask cannot have producer bit on. + EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0); + // Consumer state mask must be a single, i.e. power of 2. + EXPECT_NE(c->buffer_state_bit(), 0); + EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0); + // Consumer state mask cannot have producer bit on. + EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0); + // Consumer state mask must be a single, i.e. power of 2. + EXPECT_NE(c2->buffer_state_bit(), 0); + EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0); + // Each consumer should have unique bit. + EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0); + + // Initial state: producer not available, consumers not available. + EXPECT_EQ(0, RETRY_EINTR(p->Poll(100))); + EXPECT_EQ(0, RETRY_EINTR(c->Poll(100))); + EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100))); + EXPECT_EQ(0, p->Post(LocalHandle(), kContext)); - // Both consumers should be triggered. - EXPECT_GE(0, RETRY_EINTR(p->Poll(0))); - EXPECT_LT(0, RETRY_EINTR(c->Poll(10))); - EXPECT_LT(0, RETRY_EINTR(c2->Poll(10))); + + // New state: producer not available, consumers available. + EXPECT_EQ(0, RETRY_EINTR(p->Poll(100))); + EXPECT_EQ(1, RETRY_EINTR(c->Poll(100))); + EXPECT_EQ(1, RETRY_EINTR(c2->Poll(100))); uint64_t context; LocalHandle fence; - EXPECT_LE(0, c->Acquire(&fence, &context)); + EXPECT_EQ(0, c->Acquire(&fence, &context)); EXPECT_EQ(kContext, context); - EXPECT_GE(0, RETRY_EINTR(c->Poll(0))); + EXPECT_EQ(0, RETRY_EINTR(c->Poll(100))); + EXPECT_EQ(1, RETRY_EINTR(c2->Poll(100))); - EXPECT_LE(0, c2->Acquire(&fence, &context)); + EXPECT_EQ(0, c2->Acquire(&fence, &context)); EXPECT_EQ(kContext, context); - EXPECT_GE(0, RETRY_EINTR(c2->Poll(0))); + EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100))); + EXPECT_EQ(0, RETRY_EINTR(c->Poll(100))); EXPECT_EQ(0, c->Release(LocalHandle())); - EXPECT_GE(0, RETRY_EINTR(p->Poll(0))); + EXPECT_EQ(0, RETRY_EINTR(p->Poll(100))); EXPECT_EQ(0, c2->Discard()); - EXPECT_LE(0, RETRY_EINTR(p->Poll(0))); + EXPECT_EQ(1, RETRY_EINTR(p->Poll(100))); EXPECT_EQ(0, p->Gain(&fence)); - EXPECT_GE(0, RETRY_EINTR(p->Poll(0))); + EXPECT_EQ(0, RETRY_EINTR(p->Poll(100))); + EXPECT_EQ(0, RETRY_EINTR(c->Poll(100))); + EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100))); +} + +TEST_F(LibBufferHubTest, TestEpoll) { + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + + LocalHandle epoll_fd{epoll_create1(EPOLL_CLOEXEC)}; + ASSERT_TRUE(epoll_fd.IsValid()); + + epoll_event event; + std::array<epoll_event, 64> events; + + auto event_sources = p->GetEventSources(); + ASSERT_LT(event_sources.size(), events.size()); + + for (const auto& event_source : event_sources) { + event = {.events = event_source.event_mask | EPOLLET, + .data = {.fd = p->event_fd()}}; + ASSERT_EQ(0, epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_source.event_fd, + &event)); + } + + event_sources = c->GetEventSources(); + ASSERT_LT(event_sources.size(), events.size()); + + for (const auto& event_source : event_sources) { + event = {.events = event_source.event_mask | EPOLLET, + .data = {.fd = c->event_fd()}}; + ASSERT_EQ(0, epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_source.event_fd, + &event)); + } + + // No events should be signaled initially. + ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0)); + + // Post the producer and check for consumer signal. + EXPECT_EQ(0, p->Post({}, kContext)); + ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); + ASSERT_TRUE(events[0].events & EPOLLIN); + ASSERT_EQ(c->event_fd(), events[0].data.fd); + + // Save the event bits to translate later. + event = events[0]; + + // Check for events again. Edge-triggered mode should prevent any. + EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); + EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); + EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); + EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); + + // Translate the events. + auto event_status = c->GetEventMask(event.events); + ASSERT_TRUE(event_status); + ASSERT_TRUE(event_status.get() & EPOLLIN); + + // Check for events again. Edge-triggered mode should prevent any. + EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); +} + +TEST_F(LibBufferHubTest, TestStateMask) { + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + + // It's ok to create up to 63 consumer buffers. + uint64_t buffer_state_bits = p->buffer_state_bit(); + std::array<std::unique_ptr<BufferConsumer>, 63> cs; + for (size_t i = 0; i < 63; i++) { + cs[i] = BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(cs[i].get() != nullptr); + // Expect all buffers have unique state mask. + EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0); + buffer_state_bits |= cs[i]->buffer_state_bit(); + } + EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask); + + // The 64th creation will fail with out-of-memory error. + auto state = p->CreateConsumer(); + EXPECT_EQ(state.error(), E2BIG); + + // Release any consumer should allow us to re-create. + for (size_t i = 0; i < 63; i++) { + buffer_state_bits &= ~cs[i]->buffer_state_bit(); + cs[i] = nullptr; + cs[i] = BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(cs[i].get() != nullptr); + // The released state mask will be reused. + EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0); + buffer_state_bits |= cs[i]->buffer_state_bit(); + EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask); + } } TEST_F(LibBufferHubTest, TestStateTransitions) { @@ -98,6 +224,7 @@ TEST_F(LibBufferHubTest, TestStateTransitions) { // Release in acquired state should succeed. EXPECT_EQ(0, c->Release(LocalHandle())); + EXPECT_LT(0, RETRY_EINTR(p->Poll(10))); // Release, acquire, and post in released state should fail. EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); @@ -144,6 +271,11 @@ TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) { int64_t field1; int64_t field2; }; + struct OverSizedMetadata { + int64_t field1; + int64_t field2; + int64_t field3; + }; std::unique_ptr<BufferProducer> p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); @@ -151,9 +283,16 @@ TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) { BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - int64_t sequence = 3; - EXPECT_NE(0, p->Post(LocalHandle(), sequence)); + // It is illegal to post metadata larger than originally requested during + // buffer allocation. + OverSizedMetadata evil_meta = {}; + EXPECT_NE(0, p->Post(LocalHandle(), evil_meta)); EXPECT_GE(0, RETRY_EINTR(c->Poll(10))); + + // It is ok to post metadata smaller than originally requested during + // buffer allocation. + int64_t sequence = 42; + EXPECT_EQ(0, p->Post(LocalHandle(), sequence)); } TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { @@ -161,6 +300,11 @@ TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { int64_t field1; int64_t field2; }; + struct OverSizedMetadata { + int64_t field1; + int64_t field2; + int64_t field3; + }; std::unique_ptr<BufferProducer> p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); @@ -173,7 +317,16 @@ TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { LocalHandle fence; int64_t sequence; - EXPECT_NE(0, c->Acquire(&fence, &sequence)); + OverSizedMetadata e; + + // It is illegal to acquire metadata larger than originally requested during + // buffer allocation. + EXPECT_NE(0, c->Acquire(&fence, &e)); + + // It is ok to acquire metadata smaller than originally requested during + // buffer allocation. + EXPECT_EQ(0, c->Acquire(&fence, &sequence)); + EXPECT_EQ(m.field1, sequence); } TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) { @@ -266,12 +419,140 @@ TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) { LocalHandle fence; auto c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_NE(nullptr, c); - EXPECT_NE(-EPIPE, c->Acquire(&fence)); + EXPECT_EQ(0, p->Post<void>(LocalHandle())); + EXPECT_EQ(0, c->Acquire(&fence)); + EXPECT_EQ(0, c->Release(LocalHandle())); + EXPECT_LT(0, RETRY_EINTR(p->Poll(10))); // Test that removing persistence and closing the producer orphans the // consumer. + EXPECT_EQ(0, p->Gain(&fence)); + EXPECT_EQ(0, p->Post<void>(LocalHandle())); EXPECT_EQ(0, p->RemovePersistence()); p = nullptr; + // Orphaned consumer can acquire the posted buffer one more time in + // asynchronous manner. But synchronous call will fail. + DvrNativeBufferMetadata meta; + EXPECT_EQ(0, c->AcquireAsync(&meta, &fence)); EXPECT_EQ(-EPIPE, c->Release(LocalHandle())); } + +namespace { + +int PollFd(int fd, int timeout_ms) { + pollfd p = {fd, POLLIN, 0}; + return poll(&p, 1, timeout_ms); +} + +} // namespace + +TEST_F(LibBufferHubTest, TestAcquireFence) { + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, /*metadata_size=*/0); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + + DvrNativeBufferMetadata meta; + LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + + // Post with unsignaled fence. + EXPECT_EQ(0, p->PostAsync(&meta, f1)); + + // Should acquire a valid fence. + LocalHandle f2; + EXPECT_LT(0, RETRY_EINTR(c->Poll(10))); + EXPECT_EQ(0, c->AcquireAsync(&meta, &f2)); + EXPECT_TRUE(f2.IsValid()); + // The original fence and acquired fence should have different fd number. + EXPECT_NE(f1.Get(), f2.Get()); + EXPECT_GE(0, PollFd(f2.Get(), 0)); + + // Signal the original fence will trigger the new fence. + eventfd_write(f1.Get(), 1); + // Now the original FD has been signaled. + EXPECT_LT(0, PollFd(f2.Get(), 10)); + + // Release the consumer with an invalid fence. + EXPECT_EQ(0, c->ReleaseAsync(&meta, LocalHandle())); + + // Should gain an invalid fence. + LocalHandle f3; + EXPECT_LT(0, RETRY_EINTR(p->Poll(10))); + EXPECT_EQ(0, p->GainAsync(&meta, &f3)); + EXPECT_FALSE(f3.IsValid()); + + // Post with a signaled fence. + EXPECT_EQ(0, p->PostAsync(&meta, f1)); + + // Should acquire a valid fence and it's already signalled. + LocalHandle f4; + EXPECT_LT(0, RETRY_EINTR(c->Poll(10))); + EXPECT_EQ(0, c->AcquireAsync(&meta, &f4)); + EXPECT_TRUE(f4.IsValid()); + EXPECT_LT(0, PollFd(f4.Get(), 10)); + + // Release with an unsignalled fence and signal it immediately after release + // without producer gainning. + LocalHandle f5(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + EXPECT_EQ(0, c->ReleaseAsync(&meta, f5)); + eventfd_write(f5.Get(), 1); + + // Should gain a valid fence, which is already signaled. + LocalHandle f6; + EXPECT_LT(0, RETRY_EINTR(p->Poll(10))); + EXPECT_EQ(0, p->GainAsync(&meta, &f6)); + EXPECT_TRUE(f6.IsValid()); + EXPECT_LT(0, PollFd(f6.Get(), 10)); +} + +TEST_F(LibBufferHubTest, TestOrphanedAcquire) { + std::unique_ptr<BufferProducer> p = BufferProducer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<BufferConsumer> c1 = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c1.get() != nullptr); + const uint64_t consumer_state_bit1 = c1->buffer_state_bit(); + + DvrNativeBufferMetadata meta; + EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle())); + + LocalHandle fence; + EXPECT_LT(0, RETRY_EINTR(c1->Poll(10))); + EXPECT_LE(0, c1->AcquireAsync(&meta, &fence)); + // Destroy the consumer now will make it orphaned and the buffer is still + // acquired. + c1 = nullptr; + EXPECT_GE(0, RETRY_EINTR(p->Poll(10))); + + std::unique_ptr<BufferConsumer> c2 = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c2.get() != nullptr); + const uint64_t consumer_state_bit2 = c2->buffer_state_bit(); + EXPECT_NE(consumer_state_bit1, consumer_state_bit2); + + // The new consumer is available for acquire. + EXPECT_LT(0, RETRY_EINTR(c2->Poll(10))); + EXPECT_LE(0, c2->AcquireAsync(&meta, &fence)); + // Releasing the consumer makes the buffer gainable. + EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle())); + + // The buffer is now available for the producer to gain. + EXPECT_LT(0, RETRY_EINTR(p->Poll(10))); + + // But if another consumer is created in released state. + std::unique_ptr<BufferConsumer> c3 = + BufferConsumer::Import(p->CreateConsumer()); + ASSERT_TRUE(c3.get() != nullptr); + const uint64_t consumer_state_bit3 = c3->buffer_state_bit(); + EXPECT_NE(consumer_state_bit2, consumer_state_bit3); + // The consumer buffer is not acquirable. + EXPECT_GE(0, RETRY_EINTR(c3->Poll(10))); + EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence)); + + // Producer should be able to gain no matter what. + EXPECT_EQ(0, p->GainAsync(&meta, &fence)); +} diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h index be20e72a84..1186f9348d 100644 --- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h @@ -11,6 +11,8 @@ #include <private/dvr/ion_buffer.h> +#include "bufferhub_rpc.h" + namespace android { namespace dvr { @@ -75,6 +77,14 @@ class BufferHubBuffer : public pdx::Client { } } + std::vector<pdx::ClientChannel::EventSource> GetEventSources() const { + if (auto* client_channel = GetChannel()) { + return client_channel->GetEventSources(); + } else { + return {}; + } + } + native_handle_t* native_handle() const { return const_cast<native_handle_t*>(buffer_.handle()); } @@ -84,6 +94,10 @@ class BufferHubBuffer : public pdx::Client { int id() const { return id_; } + // A state mask which is unique to a buffer hub client among all its siblings + // sharing the same concrete graphic buffer. + uint64_t buffer_state_bit() const { return buffer_state_bit_; } + // The following methods return settings of the first buffer. Currently, // it is only possible to create multi-buffer BufferHubBuffers with the same // settings. @@ -98,6 +112,9 @@ class BufferHubBuffer : public pdx::Client { uint64_t producer_usage() const { return buffer_.usage(); } uint64_t consumer_usage() const { return buffer_.usage(); } + uint64_t GetQueueIndex() const { return metadata_header_->queue_index; } + void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; } + protected: explicit BufferHubBuffer(LocalChannelHandle channel); explicit BufferHubBuffer(const std::string& endpoint_path); @@ -106,6 +123,31 @@ class BufferHubBuffer : public pdx::Client { // Initialization helper. int ImportBuffer(); + // Check invalid metadata operation. Returns 0 if requested metadata is valid. + int CheckMetadata(size_t user_metadata_size) const; + + // Send out the new fence by updating the shared fence (shared_release_fence + // for producer and shared_acquire_fence for consumer). Note that during this + // should only be used in LocalPost() or LocalRelease, and the shared fence + // shouldn't be poll'ed by the other end. + int UpdateSharedFence(const LocalHandle& new_fence, + const LocalHandle& shared_fence); + + // IonBuffer that is shared between bufferhubd, producer, and consumers. + size_t metadata_buf_size_{0}; + size_t user_metadata_size_{0}; + BufferHubDefs::MetadataHeader* metadata_header_{nullptr}; + void* user_metadata_ptr_{nullptr}; + std::atomic<uint64_t>* buffer_state_{nullptr}; + std::atomic<uint64_t>* fence_state_{nullptr}; + + LocalHandle shared_acquire_fence_; + LocalHandle shared_release_fence_; + + // A local fence fd that holds the ownership of the fence fd on Post (for + // producer) and Release (for consumer). + LocalHandle pending_fence_fd_; + private: BufferHubBuffer(const BufferHubBuffer&) = delete; void operator=(const BufferHubBuffer&) = delete; @@ -114,8 +156,9 @@ class BufferHubBuffer : public pdx::Client { // for logging and debugging purposes only and should not be used for lookup // or any other functional purpose as a security precaution. int id_; - + uint64_t buffer_state_bit_{0ULL}; IonBuffer buffer_; + IonBuffer metadata_buffer_; }; // This represents a writable buffer. Calling Post notifies all clients and @@ -136,12 +179,17 @@ class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> { static std::unique_ptr<BufferProducer> Import( Status<LocalChannelHandle> status); + // Asynchronously posts a buffer. The fence and metadata are passed to + // consumer via shared fd and shared memory. + int PostAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence); + // Post this buffer, passing |ready_fence| to the consumers. The bytes in // |meta| are passed unaltered to the consumers. The producer must not modify // the buffer until it is re-gained. // This returns zero or a negative unix error code. int Post(const LocalHandle& ready_fence, const void* meta, - size_t meta_size_bytes); + size_t user_metadata_size); template <typename Meta, typename = typename std::enable_if<std::is_void<Meta>::value>::type> @@ -160,16 +208,15 @@ class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> { // is in the released state. // This returns zero or a negative unix error code. int Gain(LocalHandle* release_fence); + int GainAsync(); // Asynchronously marks a released buffer as gained. This method is similar to // the synchronous version above, except that it does not wait for BufferHub - // to acknowledge success or failure, nor does it transfer a release fence to - // the client. This version may be used in situations where a release fence is - // not needed. Because of the asynchronous nature of the underlying message, - // no error is returned if this method is called when the buffer is in an - // incorrect state. Returns zero if sending the message succeeded, or a - // negative errno code otherwise. - int GainAsync(); + // to acknowledge success or failure. Because of the asynchronous nature of + // the underlying message, no error is returned if this method is called when + // the buffer is in an incorrect state. Returns zero if sending the message + // succeeded, or a negative errno code if local error check fails. + int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); // Attaches the producer to |name| so that it becomes a persistent buffer that // may be retrieved by name at a later time. This may be used in cases where a @@ -216,7 +263,7 @@ class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> { BufferProducer(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, uint64_t producer_usage, uint64_t consumer_usage, - size_t meta_size_bytes); + size_t user_metadata_size); // Constructs a blob (flat) buffer with the given usage flags. BufferProducer(uint32_t usage, size_t size); @@ -234,6 +281,11 @@ class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> { // Imports the given file handle to a producer channel, taking ownership. explicit BufferProducer(LocalChannelHandle channel); + + // Local state transition helpers. + int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); + int LocalPost(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence); }; // This is a connection to a producer buffer, which can be located in another @@ -263,7 +315,7 @@ class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> { // are available. This call will only succeed if the buffer is in the posted // state. // Returns zero on success, or a negative errno code otherwise. - int Acquire(LocalHandle* ready_fence, void* meta, size_t meta_size_bytes); + int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size); // Attempt to retrieve a post event from buffer hub. If successful, // |ready_fence| is set to a fence to wait on until the buffer is ready. This @@ -274,20 +326,22 @@ class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> { return Acquire(ready_fence, meta, sizeof(*meta)); } + // Asynchronously acquires a bufer. + int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); + // This should be called after a successful Acquire call. If the fence is // valid the fence determines the buffer usage, otherwise the buffer is // released immediately. // This returns zero or a negative unix error code. int Release(const LocalHandle& release_fence); + int ReleaseAsync(); // Asynchronously releases a buffer. Similar to the synchronous version above, - // except that it does not wait for BufferHub to reply with success or error, - // nor does it transfer a release fence. This version may be used in - // situations where a release fence is not needed. Because of the asynchronous - // nature of the underlying message, no error is returned if this method is - // called when the buffer is in an incorrect state. Returns zero if sending - // the message succeeded, or a negative errno code otherwise. - int ReleaseAsync(); + // except that it does not wait for BufferHub to reply with success or error. + // The fence and metadata are passed to consumer via shared fd and shared + // memory. + int ReleaseAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence); // May be called after or instead of Acquire to indicate that the consumer // does not need to access the buffer this cycle. This returns zero or a @@ -305,6 +359,11 @@ class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> { friend BASE; explicit BufferConsumer(LocalChannelHandle channel); + + // Local state transition helpers. + int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); + int LocalRelease(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence); }; } // namespace dvr diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h index ca0e0e0820..f9fd42d7bb 100644 --- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h +++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h @@ -5,6 +5,7 @@ #include <gui/BufferQueueDefs.h> #include <sys/types.h> +#include <dvr/dvr_api.h> #include <pdx/channel_handle.h> #include <pdx/file_handle.h> #include <pdx/rpc/remote_method.h> @@ -14,6 +15,71 @@ namespace android { namespace dvr { +namespace BufferHubDefs { + +static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB; +static constexpr uint32_t kMetadataUsage = + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; + +// Single producuer multiple (up to 63) consumers ownership signal. +// 64-bit atomic unsigned int. +// +// MSB LSB +// | | +// v v +// [P|C62|...|C1|C0] +// Gain'ed state: [0|..|0|0] -> Exclusively Writable. +// Post'ed state: [1|..|0|0] +// Acquired'ed state: [1|..|X|X] -> At least one bit is set in lower 63 bits +// Released'ed state: [0|..|X|X] -> At least one bit is set in lower 63 bits +static constexpr uint64_t kProducerStateBit = 1ULL << 63; +static constexpr uint64_t kConsumerStateMask = (1ULL << 63) - 1; + +static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state, + uint64_t clear_mask, uint64_t set_mask) { + uint64_t old_state; + uint64_t new_state; + do { + old_state = buffer_state->load(); + new_state = (old_state & ~clear_mask) | set_mask; + } while (!buffer_state->compare_exchange_weak(old_state, new_state)); +} + +static inline bool IsBufferGained(uint64_t state) { return state == 0; } + +static inline bool IsBufferPosted(uint64_t state, + uint64_t consumer_bit = kConsumerStateMask) { + return (state & kProducerStateBit) && !(state & consumer_bit); +} + +static inline bool IsBufferAcquired(uint64_t state) { + return (state & kProducerStateBit) && (state & kConsumerStateMask); +} + +static inline bool IsBufferReleased(uint64_t state) { + return !(state & kProducerStateBit) && (state & kConsumerStateMask); +} + +struct __attribute__((packed, aligned(8))) MetadataHeader { + // Internal data format, which can be updated as long as the size, padding and + // field alignment of the struct is consistent within the same ABI. As this + // part is subject for future updates, it's not stable cross Android version, + // so don't have it visible from outside of the Android platform (include Apps + // and vendor HAL). + std::atomic<uint64_t> buffer_state; + std::atomic<uint64_t> fence_state; + uint64_t queue_index; + + // Public data format, which should be updated with caution. See more details + // in dvr_api.h + DvrNativeBufferMetadata metadata; +}; + +static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size"); +static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader); + +} // namespace BufferHubDefs + template <typename FileHandleType> class NativeBufferHandle { public: @@ -93,6 +159,57 @@ class NativeBufferHandle { void operator=(const NativeBufferHandle&) = delete; }; +template <typename FileHandleType> +class BufferDescription { + public: + BufferDescription() = default; + BufferDescription(const IonBuffer& buffer, const IonBuffer& metadata, int id, + uint64_t buffer_state_bit, + const FileHandleType& acquire_fence_fd, + const FileHandleType& release_fence_fd) + : id_(id), + buffer_state_bit_(buffer_state_bit), + buffer_(buffer, id), + metadata_(metadata, id), + acquire_fence_fd_(acquire_fence_fd.Borrow()), + release_fence_fd_(release_fence_fd.Borrow()) {} + + BufferDescription(BufferDescription&& other) = default; + BufferDescription& operator=(BufferDescription&& other) = default; + + // ID of the buffer client. All BufferHubBuffer clients derived from the same + // buffer in bufferhubd share the same buffer id. + int id() const { return id_; } + // State mask of the buffer client. Each BufferHubBuffer client backed by the + // same buffer channel has uniqued state bit among its siblings. For a + // producer buffer the bit must be kProducerStateBit; for a consumer the bit + // must be one of the kConsumerStateMask. + uint64_t buffer_state_bit() const { return buffer_state_bit_; } + FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); } + FileHandleType take_release_fence() { return std::move(release_fence_fd_); } + + int ImportBuffer(IonBuffer* buffer) { return buffer_.Import(buffer); } + int ImportMetadata(IonBuffer* metadata) { return metadata_.Import(metadata); } + + private: + int id_{-1}; + uint64_t buffer_state_bit_{0}; + // Two IonBuffers: one for the graphic buffer and one for metadata. + NativeBufferHandle<FileHandleType> buffer_; + NativeBufferHandle<FileHandleType> metadata_; + + // Pamameters for shared fences. + FileHandleType acquire_fence_fd_; + FileHandleType release_fence_fd_; + + PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_, + buffer_state_bit_, buffer_, metadata_, + acquire_fence_fd_, release_fence_fd_); + + BufferDescription(const BufferDescription&) = delete; + void operator=(const BufferDescription&) = delete; +}; + using BorrowedNativeBufferHandle = NativeBufferHandle<pdx::BorrowedHandle>; using LocalNativeBufferHandle = NativeBufferHandle<pdx::LocalHandle>; @@ -149,11 +266,11 @@ struct ProducerQueueConfig { // Size of the meta data associated with all the buffers allocated from the // queue. - size_t meta_size_bytes; + size_t user_metadata_size; private: PDX_SERIALIZABLE_MEMBERS(ProducerQueueConfig, is_async, default_width, - default_height, default_format, meta_size_bytes); + default_height, default_format, user_metadata_size); }; class ProducerQueueConfigBuilder { @@ -161,7 +278,7 @@ class ProducerQueueConfigBuilder { // Build a ProducerQueueConfig object. ProducerQueueConfig Build() { return {is_async_, default_width_, default_height_, default_format_, - meta_size_bytes_}; + user_metadata_size_}; } ProducerQueueConfigBuilder& SetIsAsync(bool is_async) { @@ -186,12 +303,12 @@ class ProducerQueueConfigBuilder { template <typename Meta> ProducerQueueConfigBuilder& SetMetadata() { - meta_size_bytes_ = sizeof(Meta); + user_metadata_size_ = sizeof(Meta); return *this; } - ProducerQueueConfigBuilder& SetMetadataSize(size_t meta_size_bytes) { - meta_size_bytes_ = meta_size_bytes; + ProducerQueueConfigBuilder& SetMetadataSize(size_t user_metadata_size) { + user_metadata_size_ = user_metadata_size; return *this; } @@ -200,7 +317,7 @@ class ProducerQueueConfigBuilder { uint32_t default_width_{1}; uint32_t default_height_{1}; uint32_t default_format_{1}; // PIXEL_FORMAT_RGBA_8888 - size_t meta_size_bytes_{0}; + size_t user_metadata_size_{0}; }; // Explicit specializations of ProducerQueueConfigBuilder::Build for void @@ -208,7 +325,7 @@ class ProducerQueueConfigBuilder { template <> inline ProducerQueueConfigBuilder& ProducerQueueConfigBuilder::SetMetadata<void>() { - meta_size_bytes_ = 0; + user_metadata_size_ = 0; return *this; } @@ -269,7 +386,6 @@ struct BufferHubRPC { }; // Aliases. - using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>; using LocalChannelHandle = pdx::LocalChannelHandle; using LocalHandle = pdx::LocalHandle; using Void = pdx::rpc::Void; @@ -277,25 +393,24 @@ struct BufferHubRPC { // Methods. PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer, void(uint32_t width, uint32_t height, uint32_t format, - uint64_t usage, size_t meta_size_bytes)); + uint64_t usage, size_t user_metadata_size)); PDX_REMOTE_METHOD(CreatePersistentBuffer, kOpCreatePersistentBuffer, void(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, - uint64_t usage, size_t meta_size_bytes)); + uint64_t usage, size_t user_metadata_size)); PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer, void(const std::string& name)); PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer, - NativeBufferHandle<LocalHandle>(Void)); + BufferDescription<LocalHandle>(Void)); PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void)); PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent, void(const std::string& name, int user_id, int group_id)); PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence, void(Void)); PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost, - void(LocalFence acquire_fence, MetaData)); + void(LocalFence acquire_fence)); PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void)); - PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, - std::pair<LocalFence, MetaData>(std::size_t metadata_size)); + PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, LocalFence(Void)); PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease, void(LocalFence release_fence)); PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore)); @@ -305,7 +420,7 @@ struct BufferHubRPC { QueueInfo(const ProducerQueueConfig& producer_config, const UsagePolicy& usage_policy)); PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue, - LocalChannelHandle(Void)); + LocalChannelHandle(bool silent_queue)); PDX_REMOTE_METHOD(GetQueueInfo, kOpGetQueueInfo, QueueInfo(Void)); PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers, kOpProducerQueueAllocateBuffers, diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp index bfb9a55e93..8bea0cde7a 100644 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp @@ -10,6 +10,7 @@ #include <pdx/default_transport/client_channel.h> #include <pdx/default_transport/client_channel_factory.h> #include <pdx/file_handle.h> +#include <pdx/trace.h> #define RETRY_EINTR(fnc_call) \ ([&]() -> decltype(fnc_call) { \ @@ -44,17 +45,6 @@ Status<int> PollEvents(int fd, short events) { } } -// Polls a buffer for the given events, taking care to do the proper -// translation. -Status<int> PollEvents(const std::shared_ptr<BufferHubBuffer>& buffer, - short events) { - auto poll_status = PollEvents(buffer->event_fd(), events); - if (!poll_status) - return poll_status; - - return buffer->GetEventMask(poll_status.get()); -} - std::pair<int32_t, int32_t> Unstuff(uint64_t value) { return {static_cast<int32_t>(value >> 32), static_cast<int32_t>(value & ((1ull << 32) - 1))}; @@ -115,27 +105,27 @@ void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) { default_width_ = queue_info.producer_config.default_width; default_height_ = queue_info.producer_config.default_height; default_format_ = queue_info.producer_config.default_format; - meta_size_ = queue_info.producer_config.meta_size_bytes; + user_metadata_size_ = queue_info.producer_config.user_metadata_size; id_ = queue_info.id; } std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() { - if (auto status = CreateConsumerQueueHandle()) + if (auto status = CreateConsumerQueueHandle(/*silent*/ false)) return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); else return nullptr; } std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() { - if (auto status = CreateConsumerQueueHandle()) - return std::unique_ptr<ConsumerQueue>( - new ConsumerQueue(status.take(), true)); + if (auto status = CreateConsumerQueueHandle(/*silent*/ true)) + return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); else return nullptr; } -Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle() { - auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(); +Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle( + bool silent) { + auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent); if (!status) { ALOGE( "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: " @@ -148,6 +138,7 @@ Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle() { } bool BufferHubQueue::WaitForBuffers(int timeout) { + ATRACE_NAME("BufferHubQueue::WaitForBuffers"); std::array<epoll_event, kMaxEvents> events; // Loop at least once to check for hangups. @@ -178,13 +169,18 @@ bool BufferHubQueue::WaitForBuffers(int timeout) { const int num_events = ret; // A BufferQueue's epoll fd tracks N+1 events, where there are N events, - // one for each buffer, in the queue and one extra event for the queue + // one for each buffer in the queue, and one extra event for the queue // client itself. for (int i = 0; i < num_events; i++) { int32_t event_fd; int32_t index; std::tie(event_fd, index) = Unstuff(events[i].data.u64); + PDX_TRACE_FORMAT( + "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;" + "slot=%d|", + id(), num_events, i, event_fd, index); + ALOGD_IF(TRACE, "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d", i, event_fd, index); @@ -208,6 +204,7 @@ bool BufferHubQueue::WaitForBuffers(int timeout) { Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd, int poll_events) { + ATRACE_NAME("BufferHubQueue::HandleBufferEvent"); if (!buffers_[slot]) { ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot); return ErrorStatus(ENOENT); @@ -221,58 +218,19 @@ Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd, } const int events = status.get(); + PDX_TRACE_FORMAT( + "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;" + "events=%d|", + id(), buffers_[slot]->id(), slot, event_fd, poll_events, events); + if (events & EPOLLIN) { - auto entry_status = OnBufferReady(buffers_[slot], slot); - if (entry_status.ok() || entry_status.error() == EALREADY) { - // Only enqueue the buffer if it moves to or is already in the state - // requested in OnBufferReady(). - return Enqueue(entry_status.take()); - } else if (entry_status.error() == EBUSY) { - // If the buffer is busy this means that the buffer moved from released to - // posted when a new consumer was created before the ProducerQueue had a - // chance to regain it. This is a valid transition that we have to handle - // because edge triggered poll events latch the ready state even if it is - // later de-asserted -- don't enqueue or print an error log in this case. - } else { - ALOGE( - "BufferHubQueue::HandleBufferEvent: Failed to set buffer ready, " - "queue_id=%d buffer_id=%d: %s", - id(), buffers_[slot]->id(), entry_status.GetErrorMessage().c_str()); - } + return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()}); } else if (events & EPOLLHUP) { - // Check to see if the current buffer in the slot hung up. This is a bit of - // paranoia to deal with the epoll set getting out of sync with the buffer - // slots. - auto poll_status = PollEvents(buffers_[slot], POLLIN); - if (!poll_status && poll_status.error() != ETIMEDOUT) { - ALOGE("BufferHubQueue::HandleBufferEvent: Failed to poll buffer: %s", - poll_status.GetErrorMessage().c_str()); - return poll_status.error_status(); - } - - const bool hangup_pending = status.ok() && (poll_status.get() & EPOLLHUP); - ALOGW( "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu " - "event_fd=%d buffer_id=%d hangup_pending=%d poll_status=%x", - slot, buffers_[slot]->event_fd(), buffers_[slot]->id(), hangup_pending, - poll_status.get()); - - if (hangup_pending) { - return RemoveBuffer(slot); - } else { - // Clean up the bookkeeping for the event fd. This is a bit of paranoia to - // deal with the epoll set getting out of sync with the buffer slots. - // Hitting this path should be very unusual. - const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, event_fd, nullptr); - if (ret < 0) { - ALOGE( - "BufferHubQueue::HandleBufferEvent: Failed to remove fd=%d from " - "epoll set: %s", - event_fd, strerror(-ret)); - return ErrorStatus(-ret); - } - } + "event_fd=%d buffer_id=%d", + slot, buffers_[slot]->event_fd(), buffers_[slot]->id()); + return RemoveBuffer(slot); } else { ALOGW( "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll " @@ -284,6 +242,7 @@ Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd, } Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) { + ATRACE_NAME("BufferHubQueue::HandleQueueEvent"); auto status = GetEventMask(poll_event); if (!status) { ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s", @@ -330,13 +289,16 @@ Status<void> BufferHubQueue::AddBuffer( return remove_status.error_status(); } - epoll_event event = {.events = EPOLLIN | EPOLLET, - .data = {.u64 = Stuff(buffer->event_fd(), slot)}}; - const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, buffer->event_fd(), &event); - if (ret < 0) { - ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s", - strerror(-ret)); - return ErrorStatus(-ret); + for (const auto& event_source : buffer->GetEventSources()) { + epoll_event event = {.events = event_source.event_mask | EPOLLET, + .data = {.u64 = Stuff(buffer->event_fd(), slot)}}; + const int ret = + epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event); + if (ret < 0) { + ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s", + strerror(-ret)); + return ErrorStatus(-ret); + } } buffers_[slot] = buffer; @@ -348,15 +310,16 @@ Status<void> BufferHubQueue::RemoveBuffer(size_t slot) { ALOGD_IF(TRACE, "BufferHubQueue::RemoveBuffer: slot=%zu", slot); if (buffers_[slot]) { - const int ret = - epoll_fd_.Control(EPOLL_CTL_DEL, buffers_[slot]->event_fd(), nullptr); - if (ret < 0) { - ALOGE( - "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll " - "set: " - "%s", - strerror(-ret)); - return ErrorStatus(-ret); + for (const auto& event_source : buffers_[slot]->GetEventSources()) { + const int ret = + epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr); + if (ret < 0) { + ALOGE( + "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll " + "set: %s", + strerror(-ret)); + return ErrorStatus(-ret); + } } // Trigger OnBufferRemoved callback if registered. @@ -372,7 +335,7 @@ Status<void> BufferHubQueue::RemoveBuffer(size_t slot) { Status<void> BufferHubQueue::Enqueue(Entry entry) { if (!is_full()) { - available_buffers_.Append(std::move(entry)); + available_buffers_.push(std::move(entry)); // Trigger OnBufferAvailable callback if registered. if (on_buffer_available_) @@ -385,25 +348,26 @@ Status<void> BufferHubQueue::Enqueue(Entry entry) { } } -Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue( - int timeout, size_t* slot, void* meta, LocalHandle* fence) { +Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout, + size_t* slot) { ALOGD_IF(TRACE, "BufferHubQueue::Dequeue: count=%zu, timeout=%d", count(), timeout); - if (!WaitForBuffers(timeout)) - return ErrorStatus(ETIMEDOUT); + PDX_TRACE_FORMAT("BufferHubQueue::Dequeue|count=%zu|", count()); - auto& entry = available_buffers_.Front(); + if (count() == 0) { + if (!WaitForBuffers(timeout)) + return ErrorStatus(ETIMEDOUT); + } + + auto& entry = available_buffers_.top(); + PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(), + entry.slot); std::shared_ptr<BufferHubBuffer> buffer = std::move(entry.buffer); *slot = entry.slot; - *fence = std::move(entry.fence); - if (meta && entry.metadata) { - std::copy(entry.metadata.get(), entry.metadata.get() + meta_size_, - reinterpret_cast<uint8_t*>(meta)); - } - available_buffers_.PopFront(); + available_buffers_.pop(); return {std::move(buffer)}; } @@ -417,6 +381,29 @@ void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) { on_buffer_removed_ = callback; } +pdx::Status<void> BufferHubQueue::FreeAllBuffers() { + // Clear all available buffers. + while (!available_buffers_.empty()) + available_buffers_.pop(); + + pdx::Status<void> last_error; // No error. + // Clear all buffers this producer queue is tracking. + for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { + if (buffers_[slot] != nullptr) { + auto status = RemoveBuffer(slot); + if (!status) { + ALOGE( + "ProducerQueue::FreeAllBuffers: Failed to remove buffer at " + "slot=%zu.", + slot); + last_error = status.error_status(); + } + } + } + + return last_error; +} + ProducerQueue::ProducerQueue(LocalChannelHandle handle) : BASE(std::move(handle)) { auto status = ImportQueue(); @@ -526,7 +513,7 @@ Status<void> ProducerQueue::AddBuffer( if (!status) return status; - return Enqueue(buffer, slot); + return BufferHubQueue::Enqueue({buffer, slot, 0ULL}); } Status<void> ProducerQueue::RemoveBuffer(size_t slot) { @@ -543,40 +530,33 @@ Status<void> ProducerQueue::RemoveBuffer(size_t slot) { Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue( int timeout, size_t* slot, LocalHandle* release_fence) { - if (slot == nullptr || release_fence == nullptr) { - ALOGE("ProducerQueue::Dequeue: Invalid parameter: slot=%p release_fence=%p", - slot, release_fence); - return ErrorStatus(EINVAL); - } - - auto buffer_status = - BufferHubQueue::Dequeue(timeout, slot, nullptr, release_fence); - if (!buffer_status) - return buffer_status.error_status(); - - return {std::static_pointer_cast<BufferProducer>(buffer_status.take())}; + DvrNativeBufferMetadata canonical_meta; + return Dequeue(timeout, slot, &canonical_meta, release_fence); } -Status<BufferHubQueue::Entry> ProducerQueue::OnBufferReady( - const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) { - ALOGD_IF(TRACE, - "ProducerQueue::OnBufferReady: queue_id=%d buffer_id=%d slot=%zu", - id(), buffer->id(), slot); +pdx::Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue( + int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, + pdx::LocalHandle* release_fence) { + ATRACE_NAME("ProducerQueue::Dequeue"); + if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) { + ALOGE("ProducerQueue::Dequeue: Invalid parameter."); + return ErrorStatus(EINVAL); + } - // Avoid taking a transient reference, buffer is valid for the duration of - // this method call. - auto* producer_buffer = static_cast<BufferProducer*>(buffer.get()); - LocalHandle release_fence; + auto status = BufferHubQueue::Dequeue(timeout, slot); + if (!status) + return status.error_status(); - const int ret = producer_buffer->Gain(&release_fence); - if (ret < 0) + auto buffer = std::static_pointer_cast<BufferProducer>(status.take()); + const int ret = buffer->GainAsync(out_meta, release_fence); + if (ret < 0 && ret != -EALREADY) return ErrorStatus(-ret); - else - return {{buffer, nullptr, std::move(release_fence), slot}}; + + return {std::move(buffer)}; } -ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import) - : BufferHubQueue(std::move(handle)), ignore_on_import_(ignore_on_import) { +ConsumerQueue::ConsumerQueue(LocalChannelHandle handle) + : BufferHubQueue(std::move(handle)) { auto status = ImportQueue(); if (!status) { ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s", @@ -597,9 +577,17 @@ ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import) Status<size_t> ConsumerQueue::ImportBuffers() { auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(); if (!status) { - ALOGE("ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s", + if (status.error() == EBADR) { + ALOGI( + "ConsumerQueue::ImportBuffers: Queue is silent, no buffers " + "imported."); + return {0}; + } else { + ALOGE( + "ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s", status.GetErrorMessage().c_str()); - return status.error_status(); + return status.error_status(); + } } int ret; @@ -620,22 +608,6 @@ Status<size_t> ConsumerQueue::ImportBuffers() { continue; } - // Setup ignore state before adding buffer to the queue. - if (ignore_on_import_) { - ALOGD_IF(TRACE, - "ConsumerQueue::ImportBuffers: Setting buffer to ignored state: " - "buffer_id=%d", - buffer_consumer->id()); - ret = buffer_consumer->SetIgnore(true); - if (ret < 0) { - ALOGE( - "ConsumerQueue::ImportBuffers: Failed to set ignored state on " - "imported buffer buffer_id=%d: %s", - buffer_consumer->id(), strerror(-ret)); - last_error = ErrorStatus(-ret); - } - } - auto add_status = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second); if (!add_status) { @@ -663,7 +635,7 @@ Status<void> ConsumerQueue::AddBuffer( // Check to see if the buffer is already signaled. This is necessary to catch // cases where buffers are already available; epoll edge triggered mode does - // not fire until and edge transition when adding new buffers to the epoll + // not fire until an edge transition when adding new buffers to the epoll // set. Note that we only poll the fd events because HandleBufferEvent() takes // care of checking the translated buffer events. auto poll_status = PollEvents(buffer->event_fd(), POLLIN); @@ -681,51 +653,53 @@ Status<void> ConsumerQueue::AddBuffer( } Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( - int timeout, size_t* slot, void* meta, size_t meta_size, + int timeout, size_t* slot, void* meta, size_t user_metadata_size, LocalHandle* acquire_fence) { - if (meta_size != meta_size_) { + if (user_metadata_size != user_metadata_size_) { ALOGE( "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer " "does not match metadata size (%zu) for the queue.", - meta_size, meta_size_); + user_metadata_size, user_metadata_size_); return ErrorStatus(EINVAL); } - if (slot == nullptr || acquire_fence == nullptr) { - ALOGE( - "ConsumerQueue::Dequeue: Invalid parameter: slot=%p meta=%p " - "acquire_fence=%p", - slot, meta, acquire_fence); - return ErrorStatus(EINVAL); - } + DvrNativeBufferMetadata canonical_meta; + auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence); + if (!status) + return status.error_status(); - auto buffer_status = - BufferHubQueue::Dequeue(timeout, slot, meta, acquire_fence); - if (!buffer_status) - return buffer_status.error_status(); + if (meta && user_metadata_size) { + void* metadata_src = + reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); + if (metadata_src) { + memcpy(meta, metadata_src, user_metadata_size); + } else { + ALOGW("ConsumerQueue::Dequeue: no user-defined metadata."); + } + } - return {std::static_pointer_cast<BufferConsumer>(buffer_status.take())}; + return status; } -Status<BufferHubQueue::Entry> ConsumerQueue::OnBufferReady( - const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) { - ALOGD_IF(TRACE, - "ConsumerQueue::OnBufferReady: queue_id=%d buffer_id=%d slot=%zu", - id(), buffer->id(), slot); +Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( + int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, + pdx::LocalHandle* acquire_fence) { + ATRACE_NAME("ConsumerQueue::Dequeue"); + if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) { + ALOGE("ConsumerQueue::Dequeue: Invalid parameter."); + return ErrorStatus(EINVAL); + } - // Avoid taking a transient reference, buffer is valid for the duration of - // this method call. - auto* consumer_buffer = static_cast<BufferConsumer*>(buffer.get()); - std::unique_ptr<uint8_t[]> metadata(meta_size_ ? new uint8_t[meta_size_] - : nullptr); - LocalHandle acquire_fence; + auto status = BufferHubQueue::Dequeue(timeout, slot); + if (!status) + return status.error_status(); - const int ret = - consumer_buffer->Acquire(&acquire_fence, metadata.get(), meta_size_); + auto buffer = std::static_pointer_cast<BufferConsumer>(status.take()); + const int ret = buffer->AcquireAsync(out_meta, acquire_fence); if (ret < 0) return ErrorStatus(-ret); - else - return {{buffer, std::move(metadata), std::move(acquire_fence), slot}}; + + return {std::move(buffer)}; } Status<void> ConsumerQueue::OnBufferAllocated() { diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp index 0f75a5c3d8..221bc4f9d2 100644 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp @@ -131,9 +131,9 @@ status_t BufferHubQueueProducer::setAsyncMode(bool async) { status_t BufferHubQueueProducer::dequeueBuffer( int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height, - PixelFormat format, uint64_t usage, + PixelFormat format, uint64_t usage, uint64_t* /*outBufferAge*/, FrameEventHistoryDelta* /* out_timestamps */) { - ALOGD_IF(TRACE, "dequeueBuffer: w=%u, h=%u, format=%d, usage=%llu", width, + ALOGD_IF(TRACE, "dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage); status_t ret; @@ -206,11 +206,11 @@ status_t BufferHubQueueProducer::dequeueBuffer( // It's either in free state (if the buffer has never been used before) or // in queued state (if the buffer has been dequeued and queued back to // BufferHubQueue). - // TODO(jwcai) Clean this up, make mBufferState compatible with BufferHub's - // model. - LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() && - !buffers_[slot].mBufferState.isQueued()), - "dequeueBuffer: slot %zu is not free or queued.", slot); + LOG_ALWAYS_FATAL_IF( + (!buffers_[slot].mBufferState.isFree() && + !buffers_[slot].mBufferState.isQueued()), + "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot, + buffers_[slot].mBufferState.string()); buffers_[slot].mBufferState.freeQueued(); buffers_[slot].mBufferState.dequeue(); @@ -328,7 +328,7 @@ status_t BufferHubQueueProducer::queueBuffer(int slot, LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1); - DvrNativeBufferMetadata meta_data = {}; + DvrNativeBufferMetadata meta_data; meta_data.timestamp = timestamp; meta_data.is_auto_timestamp = static_cast<int32_t>(is_auto_timestamp); meta_data.dataspace = static_cast<int32_t>(dataspace); @@ -339,7 +339,7 @@ status_t BufferHubQueueProducer::queueBuffer(int slot, meta_data.scaling_mode = static_cast<int32_t>(scaling_mode); meta_data.transform = static_cast<int32_t>(transform); - buffer_producer->Post(fence_fd, &meta_data, sizeof(meta_data)); + buffer_producer->PostAsync(&meta_data, fence_fd); buffers_[slot].mBufferState.queue(); output->width = buffer_producer->width(); @@ -384,7 +384,7 @@ status_t BufferHubQueueProducer::cancelBuffer(int slot, } auto buffer_producer = buffers_[slot].mBufferProducer; - queue_->Enqueue(buffer_producer, slot); + queue_->Enqueue(buffer_producer, slot, 0ULL); buffers_[slot].mBufferState.cancel(); buffers_[slot].mFence = fence; ALOGD_IF(TRACE, "cancelBuffer: slot %d", slot); @@ -514,6 +514,7 @@ status_t BufferHubQueueProducer::disconnect(int api, DisconnectMode /*mode*/) { return BAD_VALUE; } + FreeAllBuffers(); connected_api_ = kNoConnectedApi; return NO_ERROR; } @@ -608,6 +609,14 @@ status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const { return NO_ERROR; } +status_t BufferHubQueueProducer::getConsumerUsage(uint64_t* out_usage) const { + ALOGD_IF(TRACE, __FUNCTION__); + + // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS + *out_usage = 0; + return NO_ERROR; +} + status_t BufferHubQueueProducer::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, PixelFormat format, @@ -647,5 +656,31 @@ status_t BufferHubQueueProducer::RemoveBuffer(size_t slot) { return NO_ERROR; } +status_t BufferHubQueueProducer::FreeAllBuffers() { + for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { + // Reset in memory objects related the the buffer. + buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mBufferState.reset(); + buffers_[slot].mRequestBufferCalled = false; + buffers_[slot].mBufferProducer = nullptr; + buffers_[slot].mFence = Fence::NO_FENCE; + } + + auto status = queue_->FreeAllBuffers(); + if (!status) { + ALOGE( + "BufferHubQueueProducer::FreeAllBuffers: Failed to free all buffers on " + "the queue: %s", + status.GetErrorMessage().c_str()); + } + + if (queue_->capacity() != 0 || queue_->count() != 0) { + LOG_ALWAYS_FATAL( + "BufferHubQueueProducer::FreeAllBuffers: Not all buffers are freed."); + } + + return NO_ERROR; +} + } // namespace dvr } // namespace android diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h index d57c7af882..6962d6c9f8 100644 --- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h +++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h @@ -5,12 +5,13 @@ #include <pdx/client.h> #include <pdx/status.h> -#include <private/dvr/bufferhub_rpc.h> #include <private/dvr/buffer_hub_client.h> +#include <private/dvr/bufferhub_rpc.h> #include <private/dvr/epoll_file_descriptor.h> #include <private/dvr/ring_buffer.h> #include <memory> +#include <queue> #include <vector> namespace android { @@ -50,22 +51,30 @@ class BufferHubQueue : public pdx::Client { uint32_t default_format() const { return default_format_; } // Creates a new consumer in handle form for immediate transport over RPC. - pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(); + pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle( + bool silent = false); // Returns the number of buffers avaiable for dequeue. - size_t count() const { return available_buffers_.GetSize(); } + size_t count() const { return available_buffers_.size(); } // Returns the total number of buffers that the queue is tracking. size_t capacity() const { return capacity_; } // Returns the size of metadata structure associated with this queue. - size_t metadata_size() const { return meta_size_; } + size_t metadata_size() const { return user_metadata_size_; } // Returns whether the buffer queue is full. - bool is_full() const { return available_buffers_.IsFull(); } + bool is_full() const { + return available_buffers_.size() >= kMaxQueueCapacity; + } explicit operator bool() const { return epoll_fd_.IsValid(); } + int GetBufferId(size_t slot) const { + return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id() + : -1; + } + std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const { return buffers_[slot]; } @@ -122,13 +131,17 @@ class BufferHubQueue : public pdx::Client { // to deregister a buffer for epoll and internal bookkeeping. virtual pdx::Status<void> RemoveBuffer(size_t slot); + // Free all buffers that belongs to this queue. Can only be called from + // producer side. + virtual pdx::Status<void> FreeAllBuffers(); + // Dequeue a buffer from the free queue, blocking until one is available. The // timeout argument specifies the number of milliseconds that |Dequeue()| will // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely, // while specifying a timeout equal to zero cause Dequeue() to return // immediately, even if no buffers are available. - pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue( - int timeout, size_t* slot, void* meta, pdx::LocalHandle* fence); + pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(int timeout, + size_t* slot); // Waits for buffers to become available and adds them to the available queue. bool WaitForBuffers(int timeout); @@ -141,8 +154,9 @@ class BufferHubQueue : public pdx::Client { // per-buffer data. struct Entry { Entry() : slot(0) {} - Entry(const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) - : buffer(buffer), slot(slot) {} + Entry(const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot, + uint64_t index) + : buffer(buffer), slot(slot), index(index) {} Entry(const std::shared_ptr<BufferHubBuffer>& buffer, std::unique_ptr<uint8_t[]> metadata, pdx::LocalHandle fence, size_t slot) @@ -157,20 +171,24 @@ class BufferHubQueue : public pdx::Client { std::unique_ptr<uint8_t[]> metadata; pdx::LocalHandle fence; size_t slot; + uint64_t index; + }; + + struct EntryComparator { + bool operator()(const Entry& lhs, const Entry& rhs) { + return lhs.index > rhs.index; + } }; // Enqueues a buffer to the available list (Gained for producer or Acquireed // for consumer). pdx::Status<void> Enqueue(Entry entry); - virtual pdx::Status<Entry> OnBufferReady( - const std::shared_ptr<BufferHubBuffer>& buf, size_t slot) = 0; - // Called when a buffer is allocated remotely. virtual pdx::Status<void> OnBufferAllocated() { return {}; } // Size of the metadata that buffers in this queue cary. - size_t meta_size_{0}; + size_t user_metadata_size_{0}; private: void Initialize(); @@ -214,10 +232,12 @@ class BufferHubQueue : public pdx::Client { // Tracks the buffers belonging to this queue. Buffers are stored according to // "slot" in this vector. Each slot is a logical id of the buffer within this // queue regardless of its queue position or presence in the ring buffer. - std::vector<std::shared_ptr<BufferHubBuffer>> buffers_{kMaxQueueCapacity}; + std::array<std::shared_ptr<BufferHubBuffer>, kMaxQueueCapacity> buffers_; // Buffers and related data that are available for dequeue. - RingBuffer<Entry> available_buffers_{kMaxQueueCapacity}; + // RingBuffer<Entry> available_buffers_{kMaxQueueCapacity}; + std::priority_queue<Entry, std::vector<Entry>, EntryComparator> + available_buffers_; // Keeps track with how many buffers have been added into the queue. size_t capacity_{0}; @@ -297,16 +317,24 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { // Remove producer buffer from the queue. pdx::Status<void> RemoveBuffer(size_t slot) override; + // Free all buffers on this producer queue. + pdx::Status<void> FreeAllBuffers() override { + return BufferHubQueue::FreeAllBuffers(); + } + // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, // and caller should call Post() once it's done writing to release the buffer // to the consumer side. pdx::Status<std::shared_ptr<BufferProducer>> Dequeue( int timeout, size_t* slot, pdx::LocalHandle* release_fence); + pdx::Status<std::shared_ptr<BufferProducer>> Dequeue( + int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, + pdx::LocalHandle* release_fence); // Enqueues a producer buffer in the queue. pdx::Status<void> Enqueue(const std::shared_ptr<BufferProducer>& buffer, - size_t slot) { - return BufferHubQueue::Enqueue({buffer, slot}); + size_t slot, uint64_t index) { + return BufferHubQueue::Enqueue({buffer, slot, index}); } private: @@ -317,9 +345,6 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { // arguments as the constructors. explicit ProducerQueue(pdx::LocalChannelHandle handle); ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage); - - pdx::Status<Entry> OnBufferReady( - const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) override; }; class ConsumerQueue : public BufferHubQueue { @@ -338,10 +363,9 @@ class ConsumerQueue : public BufferHubQueue { // used to avoid participation in the buffer lifecycle by a consumer queue // that is only used to spawn other consumer queues, such as in an // intermediate service. - static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle, - bool ignore_on_import = false) { + static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle) { return std::unique_ptr<ConsumerQueue>( - new ConsumerQueue(std::move(handle), ignore_on_import)); + new ConsumerQueue(std::move(handle))); } // Import newly created buffers from the service side. @@ -365,13 +389,16 @@ class ConsumerQueue : public BufferHubQueue { } pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue( - int timeout, size_t* slot, void* meta, size_t meta_size, + int timeout, size_t* slot, void* meta, size_t user_metadata_size, + pdx::LocalHandle* acquire_fence); + pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue( + int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, pdx::LocalHandle* acquire_fence); private: friend BufferHubQueue; - ConsumerQueue(pdx::LocalChannelHandle handle, bool ignore_on_import = false); + ConsumerQueue(pdx::LocalChannelHandle handle); // Add a consumer buffer to populate the queue. Once added, a consumer buffer // is NOT available to use until the producer side |Post| it. |WaitForBuffers| @@ -380,14 +407,7 @@ class ConsumerQueue : public BufferHubQueue { pdx::Status<void> AddBuffer(const std::shared_ptr<BufferConsumer>& buffer, size_t slot); - pdx::Status<Entry> OnBufferReady( - const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) override; - pdx::Status<void> OnBufferAllocated() override; - - // Flag indicating that imported (consumer) buffers should be ignored when - // imported to avoid participating in the buffer ownership flow. - bool ignore_on_import_; }; } // namespace dvr diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h index 638a56caef..7ed55fb533 100644 --- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h +++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h @@ -43,6 +43,7 @@ class BufferHubQueueProducer : public BnGraphicBufferProducer { // See |IGraphicBufferProducer::dequeueBuffer| status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, + uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) override; // See |IGraphicBufferProducer::detachBuffer| @@ -111,6 +112,9 @@ class BufferHubQueueProducer : public BnGraphicBufferProducer { // See |IGraphicBufferProducer::getUniqueId| status_t getUniqueId(uint64_t* out_id) const override; + // See |IGraphicBufferProducer::getConsumerUsage| + status_t getConsumerUsage(uint64_t* out_usage) const override; + private: using LocalHandle = pdx::LocalHandle; @@ -131,6 +135,10 @@ class BufferHubQueueProducer : public BnGraphicBufferProducer { // Remove a buffer via BufferHubRPC. status_t RemoveBuffer(size_t slot); + // Free all buffers which are owned by the prodcuer. Note that if graphic + // buffers are acquired by the consumer, we can't . + status_t FreeAllBuffers(); + // Concreate implementation backed by BufferHubBuffer. std::shared_ptr<ProducerQueue> queue_; diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp index 5a3c3a636b..d8a9b90ad6 100644 --- a/libs/vr/libbufferhubqueue/tests/Android.bp +++ b/libs/vr/libbufferhubqueue/tests/Android.bp @@ -1,4 +1,7 @@ +header_libraries = [ + "libdvr_headers", +] shared_libraries = [ "libbase", @@ -21,6 +24,7 @@ static_libraries = [ cc_test { srcs: ["buffer_hub_queue-test.cpp"], + header_libs: header_libraries, static_libs: static_libraries, shared_libs: shared_libraries, cflags: [ @@ -38,6 +42,7 @@ cc_test { cc_test { srcs: ["buffer_hub_queue_producer-test.cpp"], + header_libs: header_libraries, static_libs: static_libraries, shared_libs: shared_libraries, cflags: [ diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp index e0a4052ec2..8a72531ed5 100644 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp +++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp @@ -3,6 +3,8 @@ #include <private/dvr/buffer_hub_queue_client.h> #include <gtest/gtest.h> +#include <poll.h> +#include <sys/eventfd.h> #include <vector> @@ -46,9 +48,9 @@ class BufferHubQueueTest : public ::testing::Test { void AllocateBuffer(size_t* slot_out = nullptr) { // Create producer buffer. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage); + auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, + kBufferLayerCount, + kBufferFormat, kBufferUsage); ASSERT_TRUE(status.ok()); size_t slot = status.take(); @@ -56,6 +58,23 @@ class BufferHubQueueTest : public ::testing::Test { *slot_out = slot; } + bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) { + pollfd pfd{queue->queue_fd(), POLLIN, 0}; + int ret; + do { + ret = poll(&pfd, 1, timeout_ms); + } while (ret == -1 && errno == EINTR); + + if (ret < 0) { + ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(), + strerror(errno)); + return false; + } else if (ret == 0) { + return false; + } + return queue->HandleQueueEvents(); + } + protected: ProducerQueueConfigBuilder config_builder_; std::unique_ptr<ProducerQueue> producer_queue_; @@ -75,7 +94,7 @@ TEST_F(BufferHubQueueTest, TestDequeue) { for (size_t i = 0; i < nb_dequeue_times; i++) { size_t slot; LocalHandle fence; - auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); @@ -113,31 +132,26 @@ TEST_F(BufferHubQueueTest, TestProducerConsumer) { // Dequeue returns timeout since no buffer is ready to consumer, but // this implicitly triggers buffer import and bump up |capacity|. LocalHandle fence; - auto status = consumer_queue_->Dequeue(0, &slot, &seq, &fence); + auto status = consumer_queue_->Dequeue(100, &slot, &seq, &fence); ASSERT_FALSE(status.ok()); ASSERT_EQ(ETIMEDOUT, status.error()); ASSERT_EQ(consumer_queue_->capacity(), i + 1); } - // Use /dev/zero as a stand-in for a fence. As long as BufferHub does not need - // to merge fences, which only happens when multiple consumers release the - // same buffer with release fences, the file object should simply pass - // through. - LocalHandle post_fence("/dev/zero", O_RDONLY); - struct stat post_fence_stat; - ASSERT_EQ(0, fstat(post_fence.Get(), &post_fence_stat)); + // Use eventfd as a stand-in for a fence. + LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); for (size_t i = 0; i < kBufferCount; i++) { LocalHandle fence; // First time there is no buffer available to dequeue. - auto consumer_status = consumer_queue_->Dequeue(0, &slot, &seq, &fence); + auto consumer_status = consumer_queue_->Dequeue(100, &slot, &seq, &fence); ASSERT_FALSE(consumer_status.ok()); ASSERT_EQ(ETIMEDOUT, consumer_status.error()); // Make sure Producer buffer is POSTED so that it's ready to Accquire // in the consumer's Dequeue() function. - auto producer_status = producer_queue_->Dequeue(0, &slot, &fence); + auto producer_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); auto producer = producer_status.take(); ASSERT_NE(nullptr, producer); @@ -147,20 +161,10 @@ TEST_F(BufferHubQueueTest, TestProducerConsumer) { // Second time the just the POSTED buffer should be dequeued. uint64_t seq_out = 0; - consumer_status = consumer_queue_->Dequeue(0, &slot, &seq_out, &fence); + consumer_status = consumer_queue_->Dequeue(100, &slot, &seq_out, &fence); ASSERT_TRUE(consumer_status.ok()); EXPECT_TRUE(fence.IsValid()); - struct stat acquire_fence_stat; - ASSERT_EQ(0, fstat(fence.Get(), &acquire_fence_stat)); - - // The file descriptors should refer to the same file object. Testing the - // device id and inode is a proxy for testing that the fds refer to the same - // file object. - EXPECT_NE(post_fence.Get(), fence.Get()); - EXPECT_EQ(post_fence_stat.st_dev, acquire_fence_stat.st_dev); - EXPECT_EQ(post_fence_stat.st_ino, acquire_fence_stat.st_ino); - auto consumer = consumer_status.take(); ASSERT_NE(nullptr, consumer); ASSERT_EQ(seq_in, seq_out); @@ -196,12 +200,11 @@ TEST_F(BufferHubQueueTest, TestRemoveBuffer) { for (size_t i = 0; i < kBufferCount; i++) { Entry* entry = &buffers[i]; - auto producer_status = - producer_queue_->Dequeue(0, &entry->slot, &entry->fence); + auto producer_status = producer_queue_->Dequeue( + /*timeout_ms=*/100, &entry->slot, &entry->fence); ASSERT_TRUE(producer_status.ok()); entry->buffer = producer_status.take(); ASSERT_NE(nullptr, entry->buffer); - EXPECT_EQ(i, entry->slot); } // Remove a buffer and make sure both queues reflect the change. @@ -218,8 +221,8 @@ TEST_F(BufferHubQueueTest, TestRemoveBuffer) { buffers[0].buffer = nullptr; // Now the consumer queue should know it's gone. - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity()); + EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), /*timeout_ms=*/100)); + ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity()); // Allocate a new buffer. This should take the first empty slot. size_t slot; @@ -286,17 +289,20 @@ TEST_F(BufferHubQueueTest, TestMultipleConsumers) { auto silent_queue = producer_queue_->CreateSilentConsumerQueue(); ASSERT_NE(nullptr, silent_queue); - // Check that buffers are correctly imported on construction. - EXPECT_EQ(kBufferCount, silent_queue->capacity()); + // Check that silent queue doesn't import buffers on creation. + EXPECT_EQ(0, silent_queue->capacity()); // Dequeue and post a buffer. size_t slot; LocalHandle fence; - auto producer_status = producer_queue_->Dequeue(0, &slot, &fence); + auto producer_status = + producer_queue_->Dequeue(/*timeout_ms=*/100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); auto producer_buffer = producer_status.take(); ASSERT_NE(nullptr, producer_buffer); ASSERT_EQ(0, producer_buffer->Post<void>({})); + // After post, check the number of remaining available buffers. + EXPECT_EQ(kBufferCount - 1, producer_queue_->count()); // Currently we expect no buffer to be available prior to calling // WaitForBuffers/HandleQueueEvents. @@ -314,23 +320,30 @@ TEST_F(BufferHubQueueTest, TestMultipleConsumers) { EXPECT_EQ(1u, consumer_queue_->count()); // Reclaim released/ignored buffers. - producer_queue_->HandleQueueEvents(); + ASSERT_EQ(kBufferCount - 1, producer_queue_->count()); + + usleep(10000); + WaitAndHandleOnce(producer_queue_.get(), /*timeout_ms=*/100); ASSERT_EQ(kBufferCount - 1, producer_queue_->count()); // Post another buffer. - producer_status = producer_queue_->Dequeue(0, &slot, &fence); + producer_status = producer_queue_->Dequeue(/*timeout_ms=*/100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(nullptr, producer_buffer); ASSERT_EQ(0, producer_buffer->Post<void>({})); // Verify that the consumer queue receives it. - EXPECT_EQ(1u, consumer_queue_->count()); - EXPECT_TRUE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(2u, consumer_queue_->count()); + size_t consumer_queue_count = consumer_queue_->count(); + WaitAndHandleOnce(consumer_queue_.get(), /*timeout_ms=*/100); + EXPECT_LT(consumer_queue_count, consumer_queue_->count()); + + // Save the current consumer queue buffer count to compare after the dequeue. + consumer_queue_count = consumer_queue_->count(); // Dequeue and acquire/release (discard) buffers on the consumer end. - auto consumer_status = consumer_queue_->Dequeue(0, &slot, &fence); + auto consumer_status = + consumer_queue_->Dequeue(/*timeout_ms=*/100, &slot, &fence); ASSERT_TRUE(consumer_status.ok()); auto consumer_buffer = consumer_status.take(); ASSERT_NE(nullptr, consumer_buffer); @@ -338,7 +351,7 @@ TEST_F(BufferHubQueueTest, TestMultipleConsumers) { // Buffer should be returned to the producer queue without being handled by // the silent consumer queue. - EXPECT_EQ(1u, consumer_queue_->count()); + EXPECT_GT(consumer_queue_count, consumer_queue_->count()); EXPECT_EQ(kBufferCount - 2, producer_queue_->count()); EXPECT_TRUE(producer_queue_->HandleQueueEvents()); EXPECT_EQ(kBufferCount - 1, producer_queue_->count()); @@ -362,13 +375,13 @@ TEST_F(BufferHubQueueTest, TestMetadata) { for (auto mi : ms) { size_t slot; LocalHandle fence; - auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0); TestMetadata mo; - auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence); + auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence); ASSERT_TRUE(c1_status.ok()); auto c1 = c1_status.take(); ASSERT_EQ(mi.a, mo.a); @@ -387,7 +400,7 @@ TEST_F(BufferHubQueueTest, TestMetadataMismatch) { int64_t mi = 3; size_t slot; LocalHandle fence; - auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); @@ -395,7 +408,7 @@ TEST_F(BufferHubQueueTest, TestMetadataMismatch) { int32_t mo; // Acquire a buffer with mismatched metadata is not OK. - auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence); + auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence); ASSERT_FALSE(c1_status.ok()); } @@ -406,14 +419,14 @@ TEST_F(BufferHubQueueTest, TestEnqueue) { size_t slot; LocalHandle fence; - auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); int64_t mo; - producer_queue_->Enqueue(p1, slot); - auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence); + producer_queue_->Enqueue(p1, slot, 0ULL); + auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence); ASSERT_FALSE(c1_status.ok()); } @@ -424,14 +437,14 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { size_t s1; AllocateBuffer(); LocalHandle fence; - auto p1_status = producer_queue_->Dequeue(0, &s1, &fence); + auto p1_status = producer_queue_->Dequeue(100, &s1, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); // producer queue is exhausted size_t s2; - auto p2_status = producer_queue_->Dequeue(0, &s2, &fence); + auto p2_status = producer_queue_->Dequeue(100, &s2, &fence); ASSERT_FALSE(p2_status.ok()); ASSERT_EQ(ETIMEDOUT, p2_status.error()); @@ -441,7 +454,7 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { ASSERT_EQ(producer_queue_->capacity(), 2U); // now we can dequeue again - p2_status = producer_queue_->Dequeue(0, &s2, &fence); + p2_status = producer_queue_->Dequeue(100, &s2, &fence); ASSERT_TRUE(p2_status.ok()); auto p2 = p2_status.take(); ASSERT_NE(nullptr, p2); @@ -456,7 +469,7 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { int64_t seq = 1; ASSERT_EQ(p1->Post(LocalHandle(), seq), 0); size_t cs1, cs2; - auto c1_status = consumer_queue_->Dequeue(0, &cs1, &seq, &fence); + auto c1_status = consumer_queue_->Dequeue(100, &cs1, &seq, &fence); ASSERT_TRUE(c1_status.ok()); auto c1 = c1_status.take(); ASSERT_NE(nullptr, c1); @@ -465,7 +478,7 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { ASSERT_EQ(cs1, s1); ASSERT_EQ(p2->Post(LocalHandle(), seq), 0); - auto c2_status = consumer_queue_->Dequeue(0, &cs2, &seq, &fence); + auto c2_status = consumer_queue_->Dequeue(100, &cs2, &seq, &fence); ASSERT_TRUE(c2_status.ok()); auto c2 = c2_status.take(); ASSERT_NE(nullptr, c2); @@ -485,7 +498,7 @@ TEST_F(BufferHubQueueTest, TestUsageSetMask) { LocalHandle fence; size_t slot; - auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_EQ(p1->usage() & set_mask, set_mask); @@ -504,7 +517,7 @@ TEST_F(BufferHubQueueTest, TestUsageClearMask) { LocalHandle fence; size_t slot; - auto p1_status = producer_queue_->Dequeue(0, &slot, &fence); + auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_EQ(0u, p1->usage() & clear_mask); @@ -543,9 +556,9 @@ TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) { ASSERT_TRUE(status.ok()); // While allocation without those bits should fail. - status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage & ~deny_clear_mask); + status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, + kBufferLayerCount, kBufferFormat, + kBufferUsage & ~deny_clear_mask); ASSERT_FALSE(status.ok()); ASSERT_EQ(EINVAL, status.error()); } @@ -570,6 +583,103 @@ TEST_F(BufferHubQueueTest, TestQueueInfo) { EXPECT_EQ(consumer_queue_->is_async(), kIsAsync); } +TEST_F(BufferHubQueueTest, TestFreeAllBuffers) { + constexpr size_t kBufferCount = 2; + +#define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers) \ + EXPECT_EQ(consumer_queue_->count(), 0U); \ + EXPECT_EQ(consumer_queue_->capacity(), 0U); \ + EXPECT_EQ(producer_queue_->count(), 0U); \ + EXPECT_EQ(producer_queue_->capacity(), 0U); \ + for (size_t i = 0; i < num_buffers; i++) { \ + AllocateBuffer(); \ + } \ + EXPECT_EQ(producer_queue_->count(), num_buffers); \ + EXPECT_EQ(producer_queue_->capacity(), num_buffers); + + size_t slot; + uint64_t seq; + LocalHandle fence; + pdx::Status<void> status; + pdx::Status<std::shared_ptr<BufferConsumer>> consumer_status; + pdx::Status<std::shared_ptr<BufferProducer>> producer_status; + std::shared_ptr<BufferConsumer> consumer_buffer; + std::shared_ptr<BufferProducer> producer_buffer; + + ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(), + UsagePolicy{})); + + // Free all buffers when buffers are avaible for dequeue. + CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); + status = producer_queue_->FreeAllBuffers(); + EXPECT_TRUE(status.ok()); + + // Free all buffers when one buffer is dequeued. + CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); + producer_status = producer_queue_->Dequeue(100, &slot, &fence); + ASSERT_TRUE(producer_status.ok()); + status = producer_queue_->FreeAllBuffers(); + EXPECT_TRUE(status.ok()); + + // Free all buffers when all buffers are dequeued. + CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); + for (size_t i = 0; i < kBufferCount; i++) { + producer_status = producer_queue_->Dequeue(100, &slot, &fence); + ASSERT_TRUE(producer_status.ok()); + } + status = producer_queue_->FreeAllBuffers(); + EXPECT_TRUE(status.ok()); + + // Free all buffers when one buffer is posted. + CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); + producer_status = producer_queue_->Dequeue(100, &slot, &fence); + ASSERT_TRUE(producer_status.ok()); + producer_buffer = producer_status.take(); + ASSERT_NE(nullptr, producer_buffer); + ASSERT_EQ(0, producer_buffer->Post(fence, &seq, sizeof(seq))); + status = producer_queue_->FreeAllBuffers(); + EXPECT_TRUE(status.ok()); + + // Free all buffers when all buffers are posted. + CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); + for (size_t i = 0; i < kBufferCount; i++) { + producer_status = producer_queue_->Dequeue(100, &slot, &fence); + ASSERT_TRUE(producer_status.ok()); + producer_buffer = producer_status.take(); + ASSERT_NE(nullptr, producer_buffer); + ASSERT_EQ(0, producer_buffer->Post(fence, &seq, sizeof(seq))); + } + status = producer_queue_->FreeAllBuffers(); + EXPECT_TRUE(status.ok()); + + // Free all buffers when all buffers are acquired. + CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); + for (size_t i = 0; i < kBufferCount; i++) { + producer_status = producer_queue_->Dequeue(100, &slot, &fence); + ASSERT_TRUE(producer_status.ok()); + producer_buffer = producer_status.take(); + ASSERT_NE(nullptr, producer_buffer); + ASSERT_EQ(0, producer_buffer->Post(fence, &seq, sizeof(seq))); + consumer_status = consumer_queue_->Dequeue(100, &slot, &seq, &fence); + ASSERT_TRUE(consumer_status.ok()); + } + + status = producer_queue_->FreeAllBuffers(); + EXPECT_TRUE(status.ok()); + + // In addition to FreeAllBuffers() from the queue, it is also required to + // delete all references to the ProducerBuffer (i.e. the PDX client). + producer_buffer = nullptr; + + // Crank consumer queue events to pickup EPOLLHUP events on the queue. + consumer_queue_->HandleQueueEvents(); + + // One last check. + CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); + +#undef CHECK_NO_BUFFER_THEN_ALLOCATE +} + } // namespace } // namespace dvr diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp index c7692d05b0..28cd63af2d 100644 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp +++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp @@ -117,9 +117,9 @@ class BufferHubQueueProducerTest : public ::testing::Test { ASSERT_NE(nullptr, outSlot); ASSERT_NE(nullptr, outFence); - int ret = mProducer->dequeueBuffer(outSlot, outFence, kDefaultWidth, - kDefaultHeight, kDefaultFormat, - kTestProducerUsageBits, nullptr); + int ret = mProducer->dequeueBuffer( + outSlot, outFence, kDefaultWidth, kDefaultHeight, kDefaultFormat, + kTestProducerUsageBits, nullptr, nullptr); // BUFFER_NEEDS_REALLOCATION can be either on or off. ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret); @@ -440,9 +440,10 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { sp<Fence> fence; for (int i = 0; i < 2; i++) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & - (mProducer->dequeueBuffer( - &slot, &fence, kDefaultWidth, kDefaultHeight, - kDefaultFormat, kTestProducerUsageBits, nullptr))) + (mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth, + kDefaultHeight, kDefaultFormat, + kTestProducerUsageBits, + nullptr, nullptr))) << "slot: " << slot; } @@ -458,7 +459,8 @@ TEST_F(BufferHubQueueProducerTest, ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth, kDefaultHeight, kDefaultFormat, - kTestProducerUsageBits, nullptr)); + kTestProducerUsageBits, + nullptr, nullptr)); } TEST_F(BufferHubQueueProducerTest, @@ -506,6 +508,44 @@ TEST_F(BufferHubQueueProducerTest, ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE)); } +TEST_F(BufferHubQueueProducerTest, ConnectDisconnectReconnect) { + int slot = -1; + sp<GraphicBuffer> buffer; + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_NO_FATAL_FAILURE(ConnectProducer()); + + constexpr int maxDequeuedBuffers = 1; + int minUndequeuedBuffers; + EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers)); + EXPECT_EQ(NO_ERROR, mProducer->setAsyncMode(false)); + EXPECT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers)); + + int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers; + + // Dequeue, request, and queue all buffers. + for (int i = 0; i < maxCapacity; i++) { + EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); + } + + // Disconnect then reconnect. + EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + EXPECT_NO_FATAL_FAILURE(ConnectProducer()); + + // Dequeue, request, and queue all buffers. + for (int i = 0; i < maxCapacity; i++) { + EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); + EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); + } + + EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); +} + } // namespace } // namespace dvr diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp index 0d23b62d28..04418d2636 100644 --- a/libs/vr/libdvr/Android.bp +++ b/libs/vr/libdvr/Android.bp @@ -34,6 +34,7 @@ srcs = [ "dvr_display_manager.cpp", "dvr_hardware_composer_client.cpp", "dvr_performance.cpp", + "dvr_pose.cpp", "dvr_surface.cpp", "dvr_vsync.cpp", ] diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp index 4d9b215361..1a9923444e 100644 --- a/libs/vr/libdvr/dvr_buffer.cpp +++ b/libs/vr/libdvr/dvr_buffer.cpp @@ -44,7 +44,13 @@ void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer) { } void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) { - delete write_buffer; + if (write_buffer != nullptr) { + ALOGW_IF( + write_buffer->slot != -1, + "dvrWriteBufferDestroy: Destroying a buffer associated with a valid " + "buffer queue slot. This may indicate possible leaks."); + delete write_buffer; + } } int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) { @@ -107,7 +113,15 @@ void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer) { *read_buffer = new DvrReadBuffer; } -void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { delete read_buffer; } +void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { + if (read_buffer != nullptr) { + ALOGW_IF( + read_buffer->slot != -1, + "dvrReadBufferDestroy: Destroying a buffer associated with a valid " + "buffer queue slot. This may indicate possible leaks."); + delete read_buffer; + } +} int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) { return read_buffer && read_buffer->read_buffer; diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp index 018abbb2a7..09a49dd713 100644 --- a/libs/vr/libdvr/dvr_buffer_queue.cpp +++ b/libs/vr/libdvr/dvr_buffer_queue.cpp @@ -14,6 +14,8 @@ using android::dvr::BufferHubQueueProducer; using android::dvr::BufferProducer; using android::dvr::ConsumerQueue; using android::dvr::ProducerQueue; +using android::dvr::ProducerQueueConfigBuilder; +using android::dvr::UsagePolicy; extern "C" { @@ -25,15 +27,6 @@ DvrWriteBufferQueue::DvrWriteBufferQueue( format_(producer_queue->default_format()) {} int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) { - if (producer_queue_->metadata_size() != sizeof(DvrNativeBufferMetadata)) { - ALOGE( - "DvrWriteBufferQueue::GetNativeWindow: The size of buffer metadata " - "(%zu) of the write queue does not match of size of " - "DvrNativeBufferMetadata (%zu).", - producer_queue_->metadata_size(), sizeof(DvrNativeBufferMetadata)); - return -EINVAL; - } - if (native_window_ == nullptr) { // Lazy creation of |native_window|, as not everyone is using // DvrWriteBufferQueue as an external surface. @@ -62,9 +55,26 @@ int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) { int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd) { + DvrNativeBufferMetadata meta; + DvrWriteBuffer* buffer = nullptr; + int fence_fd = -1; + if (const int ret = GainBuffer(timeout, &buffer, &meta, &fence_fd)) + return ret; + if (!buffer) + return -ENOMEM; + + write_buffers_[buffer->slot].reset(buffer); + write_buffer->write_buffer = std::move(buffer->write_buffer); + *out_fence_fd = fence_fd; + return 0; +} + +int DvrWriteBufferQueue::GainBuffer(int timeout, + DvrWriteBuffer** out_write_buffer, + DvrNativeBufferMetadata* out_meta, + int* out_fence_fd) { size_t slot; - pdx::LocalHandle fence; - std::shared_ptr<BufferProducer> buffer_producer; + pdx::LocalHandle release_fence; // Need to retry N+1 times, where N is total number of buffers in the queue. // As in the worst case, we will dequeue all N buffers and reallocate them, on @@ -73,15 +83,29 @@ int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer, size_t retry = 0; for (; retry < max_retries; retry++) { - auto buffer_status = producer_queue_->Dequeue(timeout, &slot, &fence); + auto buffer_status = + producer_queue_->Dequeue(timeout, &slot, out_meta, &release_fence); if (!buffer_status) { ALOGE_IF(buffer_status.error() != ETIMEDOUT, - "DvrWriteBufferQueue::Dequeue: Failed to dequeue buffer: %s", + "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer: %s", buffer_status.GetErrorMessage().c_str()); return -buffer_status.error(); } - buffer_producer = buffer_status.take(); + if (write_buffers_[slot] == nullptr) { + // Lazy initialization of a write_buffers_ slot. Note that a slot will + // only be dynamically allocated once during the entire cycle life of a + // queue. + write_buffers_[slot] = std::make_unique<DvrWriteBuffer>(); + write_buffers_[slot]->slot = slot; + } + + LOG_ALWAYS_FATAL_IF( + write_buffers_[slot]->write_buffer, + "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot); + write_buffers_[slot]->write_buffer = std::move(buffer_status.take()); + + const auto& buffer_producer = write_buffers_[slot]->write_buffer; if (!buffer_producer) return -ENOMEM; @@ -120,6 +144,9 @@ int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer, remove_status.GetErrorMessage().c_str()); return -remove_status.error(); } + // Make sure that the previously allocated buffer is dereferenced from + // write_buffers_ array. + write_buffers_[slot]->write_buffer = nullptr; auto allocate_status = producer_queue_->AllocateBuffer( width_, height_, old_layer_count, format_, old_usage); @@ -137,8 +164,52 @@ int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer, return -ENOMEM; } - write_buffer->write_buffer = std::move(buffer_producer); - *out_fence_fd = fence.Release(); + *out_write_buffer = write_buffers_[slot].release(); + *out_fence_fd = release_fence.Release(); + + return 0; +} + +int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer, + const DvrNativeBufferMetadata* meta, + int ready_fence_fd) { + LOG_FATAL_IF( + (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()), + "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); + + // Some basic sanity checks before we put the buffer back into a slot. + size_t slot = static_cast<size_t>(write_buffer->slot); + if (write_buffers_[slot] != nullptr) { + ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot); + return -EINVAL; + } + if (write_buffer->write_buffer == nullptr) { + ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer."); + return -EINVAL; + } + if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) { + ALOGE( + "DvrWriteBufferQueue::PostBuffer: Buffer to be posted does not " + "belong to this buffer queue. Posting buffer: id=%d, buffer in " + "queue: id=%d", + write_buffer->write_buffer->id(), producer_queue_->GetBufferId(slot)); + return -EINVAL; + } + + write_buffer->write_buffer->SetQueueIndex(next_post_index_++); + pdx::LocalHandle fence(ready_fence_fd); + const int ret = write_buffer->write_buffer->PostAsync(meta, fence); + if (ret < 0) { + ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d", + ret); + return ret; + } + + // Put the DvrWriteBuffer pointer back into its slot for reuse. + write_buffers_[slot].reset(write_buffer); + // It's import to reset the write buffer client now. It should stay invalid + // until next GainBuffer on the same slot. + write_buffers_[slot]->write_buffer = nullptr; return 0; } @@ -156,6 +227,36 @@ int DvrWriteBufferQueue::ResizeBuffer(uint32_t width, uint32_t height) { return 0; } +int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format, + uint32_t layer_count, uint64_t usage, + size_t capacity, size_t metadata_size, + DvrWriteBufferQueue** out_write_queue) { + if (!out_write_queue) + return -EINVAL; + + auto config_builder = ProducerQueueConfigBuilder() + .SetDefaultWidth(width) + .SetDefaultHeight(height) + .SetDefaultFormat(format) + .SetMetadataSize(metadata_size); + std::unique_ptr<ProducerQueue> producer_queue = + ProducerQueue::Create(config_builder.Build(), UsagePolicy{}); + if (!producer_queue) { + ALOGE("dvrWriteBufferQueueCreate: Failed to create producer queue."); + return -ENOMEM; + } + + auto status = producer_queue->AllocateBuffers(width, height, layer_count, + format, usage, capacity); + if (!status.ok()) { + ALOGE("dvrWriteBufferQueueCreate: Failed to allocate buffers."); + return -ENOMEM; + } + + *out_write_queue = new DvrWriteBufferQueue(std::move(producer_queue)); + return 0; +} + void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) { delete write_queue; } @@ -176,6 +277,14 @@ int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) { int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue, ANativeWindow** out_window) { + ALOGW( + "dvrWriteBufferQueueGetExternalSurface: This API has been deprecated and " + "renamed to dvrWriteBufferQueueGetANativeWindow."); + return dvrWriteBufferQueueGetANativeWindow(write_queue, out_window); +} + +int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue, + ANativeWindow** out_window) { if (!write_queue || !out_window) return -EINVAL; @@ -199,6 +308,27 @@ int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout, return write_queue->Dequeue(timeout, write_buffer, out_fence_fd); } +int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout, + DvrWriteBuffer** out_write_buffer, + DvrNativeBufferMetadata* out_meta, + int* out_fence_fd) { + if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd) + return -EINVAL; + + return write_queue->GainBuffer(timeout, out_write_buffer, out_meta, + out_fence_fd); +} + +int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue, + DvrWriteBuffer* write_buffer, + const DvrNativeBufferMetadata* meta, + int ready_fence_fd) { + if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta) + return -EINVAL; + + return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd); +} + int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue, uint32_t width, uint32_t height) { if (!write_queue) @@ -251,6 +381,82 @@ int DvrReadBufferQueue::Dequeue(int timeout, DvrReadBuffer* read_buffer, read_buffer->read_buffer = buffer_status.take(); *out_fence_fd = acquire_fence.Release(); + + return 0; +} + +int DvrReadBufferQueue::AcquireBuffer(int timeout, + DvrReadBuffer** out_read_buffer, + DvrNativeBufferMetadata* out_meta, + int* out_fence_fd) { + size_t slot; + pdx::LocalHandle acquire_fence; + auto buffer_status = + consumer_queue_->Dequeue(timeout, &slot, out_meta, &acquire_fence); + if (!buffer_status) { + ALOGE_IF(buffer_status.error() != ETIMEDOUT, + "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer: %s", + buffer_status.GetErrorMessage().c_str()); + return -buffer_status.error(); + } + + if (read_buffers_[slot] == nullptr) { + // Lazy initialization of a read_buffers_ slot. Note that a slot will only + // be dynamically allocated once during the entire cycle life of a queue. + read_buffers_[slot] = std::make_unique<DvrReadBuffer>(); + read_buffers_[slot]->slot = slot; + } + + LOG_FATAL_IF( + read_buffers_[slot]->read_buffer, + "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot); + read_buffers_[slot]->read_buffer = std::move(buffer_status.take()); + + *out_read_buffer = read_buffers_[slot].release(); + *out_fence_fd = acquire_fence.Release(); + + return 0; +} + +int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer, + const DvrNativeBufferMetadata* meta, + int release_fence_fd) { + LOG_FATAL_IF( + (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()), + "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); + + // Some basic sanity checks before we put the buffer back into a slot. + size_t slot = static_cast<size_t>(read_buffer->slot); + if (read_buffers_[slot] != nullptr) { + ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot); + return -EINVAL; + } + if (read_buffer->read_buffer == nullptr) { + ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer."); + return -EINVAL; + } + if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) { + ALOGE( + "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released does not " + "belong to this buffer queue. Releasing buffer: id=%d, buffer in " + "queue: id=%d", + read_buffer->read_buffer->id(), consumer_queue_->GetBufferId(slot)); + return -EINVAL; + } + + pdx::LocalHandle fence(release_fence_fd); + int ret = read_buffer->read_buffer->ReleaseAsync(meta, fence); + if (ret < 0) { + ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d", + ret); + return ret; + } + + // Put the DvrReadBuffer pointer back into its slot for reuse. + read_buffers_[slot].reset(read_buffer); + // It's import to reset the read buffer client now. It should stay invalid + // until next AcquireBuffer on the same slot. + read_buffers_[slot]->read_buffer = nullptr; return 0; } @@ -271,9 +477,11 @@ void DvrReadBufferQueue::SetBufferRemovedCallback( } else { consumer_queue_->SetBufferRemovedCallback( [callback, context](const std::shared_ptr<BufferHubBuffer>& buffer) { - DvrReadBuffer read_buffer{ - std::static_pointer_cast<BufferConsumer>(buffer)}; - callback(&read_buffer, context); + // When buffer is removed from the queue, the slot is already invalid. + auto read_buffer = std::make_unique<DvrReadBuffer>(); + read_buffer->read_buffer = + std::static_pointer_cast<BufferConsumer>(buffer); + callback(read_buffer.release(), context); }); } } @@ -330,6 +538,27 @@ int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, meta_size_bytes); } +int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout, + DvrReadBuffer** out_read_buffer, + DvrNativeBufferMetadata* out_meta, + int* out_fence_fd) { + if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd) + return -EINVAL; + + return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta, + out_fence_fd); +} + +int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue, + DvrReadBuffer* read_buffer, + const DvrNativeBufferMetadata* meta, + int release_fence_fd) { + if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta) + return -EINVAL; + + return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd); +} + int dvrReadBufferQueueSetBufferAvailableCallback( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferAvailableCallback callback, void* context) { diff --git a/libs/vr/libdvr/dvr_buffer_queue_internal.h b/libs/vr/libdvr/dvr_buffer_queue_internal.h index ffbe7a5836..e53a6868ff 100644 --- a/libs/vr/libdvr/dvr_buffer_queue_internal.h +++ b/libs/vr/libdvr/dvr_buffer_queue_internal.h @@ -5,11 +5,23 @@ #include <private/dvr/buffer_hub_queue_client.h> #include <sys/cdefs.h> +#include <array> #include <memory> +#include "dvr_internal.h" + struct ANativeWindow; +typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata; +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct DvrReadBufferQueue DvrReadBufferQueue; +typedef struct DvrWriteBuffer DvrWriteBuffer; +typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context); +typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer, + void* context); + struct DvrWriteBufferQueue { + using BufferHubQueue = android::dvr::BufferHubQueue; using ProducerQueue = android::dvr::ProducerQueue; // Create a concrete object for DvrWriteBufferQueue. @@ -31,18 +43,27 @@ struct DvrWriteBufferQueue { int GetNativeWindow(ANativeWindow** out_window); int CreateReadQueue(DvrReadBufferQueue** out_read_queue); int Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd); + int GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer, + DvrNativeBufferMetadata* out_meta, int* out_fence_fd); + int PostBuffer(DvrWriteBuffer* write_buffer, + const DvrNativeBufferMetadata* meta, int ready_fence_fd); int ResizeBuffer(uint32_t width, uint32_t height); private: std::shared_ptr<ProducerQueue> producer_queue_; + std::array<std::unique_ptr<DvrWriteBuffer>, BufferHubQueue::kMaxQueueCapacity> + write_buffers_; + int64_t next_post_index_ = 0; uint32_t width_; uint32_t height_; uint32_t format_; + android::sp<android::Surface> native_window_; }; struct DvrReadBufferQueue { + using BufferHubQueue = android::dvr::BufferHubQueue; using ConsumerQueue = android::dvr::ConsumerQueue; explicit DvrReadBufferQueue( @@ -54,7 +75,11 @@ struct DvrReadBufferQueue { int CreateReadQueue(DvrReadBufferQueue** out_read_queue); int Dequeue(int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd, - void* out_meta, size_t meta_size_bytes); + void* out_meta, size_t user_metadata_size); + int AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer, + DvrNativeBufferMetadata* out_meta, int* out_fence_fd); + int ReleaseBuffer(DvrReadBuffer* read_buffer, + const DvrNativeBufferMetadata* meta, int release_fence_fd); void SetBufferAvailableCallback( DvrReadBufferQueueBufferAvailableCallback callback, void* context); void SetBufferRemovedCallback( @@ -63,6 +88,8 @@ struct DvrReadBufferQueue { private: std::shared_ptr<ConsumerQueue> consumer_queue_; + std::array<std::unique_ptr<DvrReadBuffer>, BufferHubQueue::kMaxQueueCapacity> + read_buffers_; }; #endif // ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_ diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h index 28b6c28e9f..de8bb96aec 100644 --- a/libs/vr/libdvr/dvr_internal.h +++ b/libs/vr/libdvr/dvr_internal.h @@ -34,10 +34,20 @@ DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer( extern "C" { struct DvrWriteBuffer { + // The slot nubmer of the buffer, a valid slot number must be in the range of + // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for + // DvrWriteBuffer acquired from a DvrWriteBufferQueue. + int32_t slot = -1; + std::shared_ptr<android::dvr::BufferProducer> write_buffer; }; struct DvrReadBuffer { + // The slot nubmer of the buffer, a valid slot number must be in the range of + // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for + // DvrReadBuffer acquired from a DvrReadBufferQueue. + int32_t slot = -1; + std::shared_ptr<android::dvr::BufferConsumer> read_buffer; }; diff --git a/libs/vr/libdvr/dvr_pose.cpp b/libs/vr/libdvr/dvr_pose.cpp new file mode 100644 index 0000000000..c379ef55e8 --- /dev/null +++ b/libs/vr/libdvr/dvr_pose.cpp @@ -0,0 +1,29 @@ +#include "include/dvr/dvr_pose.h" + +#include <memory> + +#include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/pose_client_internal.h> + +#include "dvr_buffer_queue_internal.h" + +using android::dvr::ConsumerQueue; + +int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type, + DvrReadBufferQueue** queue_out) { + if (!client || !queue_out) + return -EINVAL; + + ConsumerQueue* consumer_queue; + int status = android::dvr::dvrPoseClientGetDataReaderHandle(client, + data_type, + &consumer_queue); + if (status != 0) { + ALOGE("dvrPoseClientGetDataReader: Failed to get queue: %d", status); + return status; + } + + std::shared_ptr<ConsumerQueue> consumer_queue_ptr{consumer_queue}; + *queue_out = new DvrReadBufferQueue(consumer_queue_ptr); + return 0; +} diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h index d0dbd8d390..499b7c190a 100644 --- a/libs/vr/libdvr/include/dvr/dvr_api.h +++ b/libs/vr/libdvr/include/dvr/dvr_api.h @@ -15,6 +15,12 @@ extern "C" { #endif +#ifdef __GNUC__ +#define ALIGNED_DVR_STRUCT(x) __attribute__((packed, aligned(x))) +#else +#define ALIGNED_DVR_STRUCT(x) +#endif + typedef struct ANativeWindow ANativeWindow; typedef struct DvrPoseAsync DvrPoseAsync; @@ -23,6 +29,7 @@ typedef uint64_t DvrSurfaceUpdateFlags; typedef struct DvrDisplayManager DvrDisplayManager; typedef struct DvrSurfaceState DvrSurfaceState; typedef struct DvrPoseClient DvrPoseClient; +typedef struct DvrPoseDataCaptureRequest DvrPoseDataCaptureRequest; typedef struct DvrVSyncClient DvrVSyncClient; typedef struct DvrVirtualTouchpad DvrVirtualTouchpad; @@ -33,6 +40,7 @@ typedef struct AHardwareBuffer AHardwareBuffer; typedef struct DvrReadBufferQueue DvrReadBufferQueue; typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; +typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata; typedef struct DvrSurface DvrSurface; typedef uint64_t DvrSurfaceAttributeType; @@ -159,18 +167,33 @@ typedef const struct native_handle* (*DvrBufferGetNativeHandlePtr)( DvrBuffer* buffer); // dvr_buffer_queue.h +typedef int (*DvrWriteBufferQueueCreatePtr)(uint32_t width, uint32_t height, + uint32_t format, + uint32_t layer_count, + uint64_t usage, size_t capacity, + size_t metadata_size, + DvrWriteBufferQueue** queue_out); typedef void (*DvrWriteBufferQueueDestroyPtr)(DvrWriteBufferQueue* write_queue); typedef ssize_t (*DvrWriteBufferQueueGetCapacityPtr)( DvrWriteBufferQueue* write_queue); typedef int (*DvrWriteBufferQueueGetIdPtr)(DvrWriteBufferQueue* write_queue); typedef int (*DvrWriteBufferQueueGetExternalSurfacePtr)( DvrWriteBufferQueue* write_queue, ANativeWindow** out_window); +typedef int (*DvrWriteBufferQueueGetANativeWindowPtr)( + DvrWriteBufferQueue* write_queue, ANativeWindow** out_window); typedef int (*DvrWriteBufferQueueCreateReadQueuePtr)( DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue); typedef int (*DvrWriteBufferQueueDequeuePtr)(DvrWriteBufferQueue* write_queue, int timeout, DvrWriteBuffer* out_buffer, int* out_fence_fd); +typedef int (*DvrWriteBufferQueueGainBufferPtr)( + DvrWriteBufferQueue* write_queue, int timeout, + DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta, + int* out_fence_fd); +typedef int (*DvrWriteBufferQueuePostBufferPtr)( + DvrWriteBufferQueue* write_queue, DvrWriteBuffer* write_buffer, + const DvrNativeBufferMetadata* meta, int ready_fence_fd); typedef int (*DvrWriteBufferQueueResizeBufferPtr)( DvrWriteBufferQueue* write_queue, uint32_t width, uint32_t height); typedef void (*DvrReadBufferQueueDestroyPtr)(DvrReadBufferQueue* read_queue); @@ -185,6 +208,13 @@ typedef int (*DvrReadBufferQueueDequeuePtr)(DvrReadBufferQueue* read_queue, DvrReadBuffer* out_buffer, int* out_fence_fd, void* out_meta, size_t meta_size_bytes); +typedef int (*DvrReadBufferQueueAcquireBufferPtr)( + DvrReadBufferQueue* read_queue, int timeout, + DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta, + int* out_fence_fd); +typedef int (*DvrReadBufferQueueReleaseBufferPtr)( + DvrReadBufferQueue* read_queue, DvrReadBuffer* read_buffer, + const DvrNativeBufferMetadata* meta, int release_fence_fd); typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context); typedef int (*DvrReadBufferQueueSetBufferAvailableCallbackPtr)( DvrReadBufferQueue* read_queue, @@ -238,6 +268,15 @@ typedef int (*DvrPoseClientGetControllerPtr)(DvrPoseClient* client, DvrPoseAsync* out_pose); typedef int (*DvrPoseClientSensorsEnablePtr)(DvrPoseClient* client, bool enabled); +typedef int (*DvrPoseClientDataCapturePtr)(DvrPoseClient* client, + const DvrPoseDataCaptureRequest* request); +typedef int (*DvrPoseClientDataReaderDestroyPtr)(DvrPoseClient* client, + uint64_t data_type); + +// dvr_pose.h +typedef int (*DvrPoseClientGetDataReaderPtr)(DvrPoseClient* client, + uint64_t data_type, + DvrReadBufferQueue** read_queue); // services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h @@ -334,7 +373,24 @@ typedef int (*DvrPerformanceSetSchedulerPolicyPtr)( // existing data members. If new fields need to be added, please take extra care // to make sure that new data field is padded properly the size of the struct // stays same. -struct DvrNativeBufferMetadata { +struct ALIGNED_DVR_STRUCT(8) DvrNativeBufferMetadata { +#ifdef __cplusplus + DvrNativeBufferMetadata() + : timestamp(0), + is_auto_timestamp(0), + dataspace(0), + crop_left(0), + crop_top(0), + crop_right(0), + crop_bottom(0), + scaling_mode(0), + transform(0), + index(0), + user_metadata_size(0), + user_metadata_ptr(0), + release_fence_mask(0), + reserved{0} {} +#endif // Timestamp of the frame. int64_t timestamp; @@ -358,10 +414,32 @@ struct DvrNativeBufferMetadata { // android/native_window.h int32_t transform; - // Reserved bytes for so that the struct is forward compatible. - int32_t reserved[16]; + // The index of the frame. + int64_t index; + + // Size of additional metadata requested by user. + uint64_t user_metadata_size; + + // Raw memory address of the additional user defined metadata. Only valid when + // user_metadata_size is non-zero. + uint64_t user_metadata_ptr; + + // Only applicable for metadata retrieved from GainAsync. This indicates which + // consumer has pending fence that producer should epoll on. + uint64_t release_fence_mask; + + // Reserved bytes for so that the struct is forward compatible and padding to + // 104 bytes so the size is a multiple of 8. + int32_t reserved[8]; }; +#ifdef __cplusplus +// Warning: DvrNativeBufferMetadata is part of the DVR API and changing its size +// will cause compatiblity issues between different DVR API releases. +static_assert(sizeof(DvrNativeBufferMetadata) == 104, + "Unexpected size for DvrNativeBufferMetadata"); +#endif + struct DvrApi_v1 { // Defines an API entry for V1 (no version suffix). #define DVR_V1_API_ENTRY(name) Dvr##name##Ptr name diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h index 72e0f674f4..cce8c7ee40 100644 --- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h +++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h @@ -65,7 +65,7 @@ DVR_V1_API_ENTRY(BufferGlobalLayoutVersionGet); DVR_V1_API_ENTRY(WriteBufferQueueDestroy); DVR_V1_API_ENTRY(WriteBufferQueueGetCapacity); DVR_V1_API_ENTRY(WriteBufferQueueGetId); -DVR_V1_API_ENTRY(WriteBufferQueueGetExternalSurface); +DVR_V1_API_ENTRY(WriteBufferQueueGetExternalSurface); // deprecated DVR_V1_API_ENTRY(WriteBufferQueueCreateReadQueue); DVR_V1_API_ENTRY(WriteBufferQueueDequeue); DVR_V1_API_ENTRY(WriteBufferQueueResizeBuffer); @@ -160,3 +160,20 @@ DVR_V1_API_ENTRY(PoseClientSensorsEnable); // Read buffer queue DVR_V1_API_ENTRY(ReadBufferQueueGetEventFd); + +// Create write buffer queue locally +DVR_V1_API_ENTRY(WriteBufferQueueCreate); + +// Gets an ANativeWindow from DvrWriteBufferQueue. +DVR_V1_API_ENTRY(WriteBufferQueueGetANativeWindow); + +// Dvr{Read,Write}BufferQueue API for asynchronous IPC. +DVR_V1_API_ENTRY(WriteBufferQueueGainBuffer); +DVR_V1_API_ENTRY(WriteBufferQueuePostBuffer); +DVR_V1_API_ENTRY(ReadBufferQueueAcquireBuffer); +DVR_V1_API_ENTRY(ReadBufferQueueReleaseBuffer); + +// Pose client +DVR_V1_API_ENTRY(PoseClientGetDataReader); +DVR_V1_API_ENTRY(PoseClientDataCapture); +DVR_V1_API_ENTRY(PoseClientDataReaderDestroy); diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h index e2127f8e31..bf695c7dbc 100644 --- a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h +++ b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h @@ -12,6 +12,36 @@ typedef struct ANativeWindow ANativeWindow; typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; typedef struct DvrReadBufferQueue DvrReadBufferQueue; +// Creates a write buffer queue to be used locally. +// +// Note that this API is mostly for testing purpose. For now there is no +// mechanism to send a DvrWriteBufferQueue cross process. Use +// dvrSurfaceCreateWriteBufferQueue if cross-process buffer transport is +// intended. +// +// @param width The width of the buffers that this queue will produce. +// @param height The height of buffers that this queue will produce. +// @param format The format of the buffers that this queue will produce. This +// must be one of the AHARDWAREBUFFER_FORMAT_XXX enums. +// @param layer_count The number of layers of the buffers that this queue will +// produce. +// @param usage The usage of the buffers that this queue will produce. This +// must a combination of the AHARDWAREBUFFER_USAGE_XXX flags. +// @param capacity The number of buffer that this queue will allocate. Note that +// all buffers will be allocated on create. Currently, the number of buffers +// is the queue cannot be changed after creation though DVR API. However, +// ANativeWindow can choose to reallocate, attach, or detach buffers from +// a DvrWriteBufferQueue through Android platform logic. +// @param metadata_size The size of metadata in bytes. +// @param out_write_queue The pointer of a DvrWriteBufferQueue will be filled +// here if the method call succeeds. The metadata size must match +// the metadata size in dvrWriteBufferPost/dvrReadBufferAcquire. +// @return Zero on success, or negative error code. +int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format, + uint32_t layer_count, uint64_t usage, + size_t capacity, size_t metadata_size, + DvrWriteBufferQueue** out_write_queue); + // Destroy a write buffer queue. // // @param write_queue The DvrWriteBufferQueue of interest. @@ -43,6 +73,10 @@ int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue); // the method call succeeds. // @return Zero on success; or -EINVAL if this DvrWriteBufferQueue does not // support being used as an android Surface. +int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue, + ANativeWindow** out_window); + +// @deprecated Please use dvrWriteBufferQueueGetANativeWindow instead. int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue, ANativeWindow** out_window); @@ -55,21 +89,44 @@ int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue, int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue); -// Dequeue a buffer to write into. +// @deprecated Please use dvrWriteBufferQueueGainBuffer instead. +int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout, + DvrWriteBuffer* out_buffer, int* out_fence_fd); + +// Gains a buffer to write into. // -// @param write_queue The DvrWriteBufferQueue of interest. +// @param write_queue The DvrWriteBufferQueue to gain buffer from. // @param timeout Specifies the number of milliseconds that the method will // block. Specifying a timeout of -1 causes it to block indefinitely, // while specifying a timeout equal to zero cause it to return immediately, // even if no buffers are available. // @param out_buffer A targeting DvrWriteBuffer object to hold the output of the -// dequeue operation. Must be created by |dvrWriteBufferCreateEmpty|. +// dequeue operation. +// @param out_meta A DvrNativeBufferMetadata object populated by the +// corresponding dvrReadBufferQueueReleaseBuffer API. // @param out_fence_fd A sync fence fd defined in NDK's sync.h API, which // signals the release of underlying buffer. The producer should wait until // this fence clears before writing data into it. // @return Zero on success, or negative error code. -int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout, - DvrWriteBuffer* out_buffer, int* out_fence_fd); +int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout, + DvrWriteBuffer** out_write_buffer, + DvrNativeBufferMetadata* out_meta, + int* out_fence_fd); + +// Posts a buffer and signals its readiness to be read from. +// +// @param write_queue The DvrWriteBufferQueue to post buffer into. +// @param write_buffer The buffer to be posted. +// @param meta The buffer metadata describing the buffer. +// @param ready_fence_fd A sync fence fd defined in NDK's sync.h API, which +// signals the readdiness of underlying buffer. When a valid fence gets +// passed in, the consumer will wait the fence to be ready before it starts +// to ready from the buffer. +// @return Zero on success, or negative error code. +int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue, + DvrWriteBuffer* write_buffer, + const DvrNativeBufferMetadata* meta, + int ready_fence_fd); // Overrides buffer dimension with new width and height. // @@ -119,28 +176,45 @@ int dvrReadBufferQueueGetEventFd(DvrReadBufferQueue* read_queue); int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue, DvrReadBufferQueue** out_read_queue); -// Dequeue a buffer to read from. +// @deprecated Please use dvrReadBufferQueueAcquireBuffer instead. +int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, + DvrReadBuffer* out_buffer, int* out_fence_fd, + void* out_meta, size_t meta_size_bytes); + +// Dequeues a buffer to read from. // -// @param read_queue The DvrReadBufferQueue of interest. +// @param read_queue The DvrReadBufferQueue to acquire buffer from. // @param timeout Specifies the number of milliseconds that the method will // block. Specifying a timeout of -1 causes it to block indefinitely, // while specifying a timeout equal to zero cause it to return immediately, // even if no buffers are available. // @param out_buffer A targeting DvrReadBuffer object to hold the output of the // dequeue operation. Must be created by |dvrReadBufferCreateEmpty|. +// @param out_meta A DvrNativeBufferMetadata object populated by the +// corresponding dvrWriteBufferQueuePostBuffer API. // @param out_fence_fd A sync fence fd defined in NDK's sync.h API, which // signals the release of underlying buffer. The consumer should wait until // this fence clears before reading data from it. -// @param out_meta The memory area where a metadata object will be filled. -// Can be nullptr iff |meta_size_bytes| is zero (i.e., there is no -// metadata). -// @param meta_size_bytes Size of the metadata object caller expects. If it -// doesn't match the size of actually metadata transported by the buffer -// queue, the method returns -EINVAL. // @return Zero on success, or negative error code. -int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, - DvrReadBuffer* out_buffer, int* out_fence_fd, - void* out_meta, size_t meta_size_bytes); +int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout, + DvrReadBuffer** out_read_buffer, + DvrNativeBufferMetadata* out_meta, + int* out_fence_fd); + +// Releases a buffer and signals its readiness to be written into. +// +// @param read_queue The DvrReadBufferQueue to release buffer into. +// @param read_buffer The buffer to be released. +// @param meta The buffer metadata describing the buffer. +// @param release_fence_fd A sync fence fd defined in NDK's sync.h API, which +// signals the readdiness of underlying buffer. When a valid fence gets +// passed in, the producer will wait the fence to be ready before it starts +// to write into the buffer again. +// @return Zero on success, or negative error code. +int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue, + DvrReadBuffer* read_buffer, + const DvrNativeBufferMetadata* meta, + int release_fence_fd); // Callback function which will be called when a buffer is avaiable. // diff --git a/libs/vr/libdvr/include/dvr/dvr_pose.h b/libs/vr/libdvr/include/dvr/dvr_pose.h index b3df0285d7..87527515b4 100644 --- a/libs/vr/libdvr/include/dvr/dvr_pose.h +++ b/libs/vr/libdvr/include/dvr/dvr_pose.h @@ -15,6 +15,9 @@ typedef float float32x4_t __attribute__((__vector_size__(16))); #endif #endif +typedef struct DvrPoseClient DvrPoseClient; +typedef struct DvrReadBufferQueue DvrReadBufferQueue; + // Represents an estimated pose, accessed asynchronously through a shared ring // buffer. No assumptions should be made about the data in padding space. // The size of this struct is 128 bytes. @@ -95,6 +98,57 @@ typedef struct __attribute__((packed, aligned(16))) DvrPose { uint8_t padding[12]; } DvrPose; +// Represents a data type that can be streamed from pose service. +enum { + DVR_POSE_RAW_DATA_STEREO_IMAGE = (1ULL << 0), + DVR_POSE_RAW_DATA_POINT_CLOUD = (1ULL << 1), + DVR_POSE_RAW_DATA_FEATURES = (1ULL << 2), + + // Always last. + DVR_POSE_RAW_DATA_COUNT = (1ULL << 3), +}; + +// A request to retrieve data from the pose service. Expects that a buffer +// queue has been initialized through dvrPoseClientGetDataReader(). +typedef struct DvrPoseDataCaptureRequest { + // The type of data to capture. Refer to enum DVR_POSE_RAW_DATA_* for types. + uint64_t data_type; + // The sample interval. This can be used to skip samples. For example, a + // value of 5 will capture every fifth frame and discard the 4 frames in + // between. Set to 1 to capture all frames. + uint32_t sample_interval; + // The length of time to capture samples in milliseconds. Set to 0 to capture + // indefinitely. + uint32_t capture_time_ms; + // Reserved fields. + uint32_t reserved0; + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; +} DvrPoseDataCaptureRequest; + +// Gets a read buffer queue for the data type |data_type|. Each call returns a +// different read buffer queue connected to the same write buffer queue. A +// separate write buffer queue exists for each |data_type|. +// +// PoseService supports a single consumer per write buffer queue. The consumer +// is expected to hold a single DvrReadBufferQueue at a time. Callers should +// cache these instead of requesting new ones when possible. If the consumer +// disconnects from the queue, it can regain a read buffer queue for the same +// producer by calling this function. +// +// For data_type DVR_POSE_RAW_DATA_STEREO_IMAGE, each buffer consists of two +// images formatted as a AHARDWAREBUFFER_FORMAT_BLOB, where height is 1 and +// width is the total size of both images. The size of an individual image can +// be found in the metadata struct DvrNativeBufferMetadata, where width is +// |crop_right| and height is |crop_bottom|/2. Each image is contiguous in +// memory with stride equal to width. +int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type, + DvrReadBufferQueue** queue_out); + +// TODO(b/65067592): Move pose api's from pose_client.h to here. + __END_DECLS #endif // ANDROID_DVR_PUBLIC_POSE_H_ diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp index ab2ee75a3e..887766a535 100644 --- a/libs/vr/libdvr/tests/Android.bp +++ b/libs/vr/libdvr/tests/Android.bp @@ -42,11 +42,13 @@ cc_test { "dvr_named_buffer-test.cpp", ], + header_libs: ["libdvr_headers"], static_libs: static_libraries, shared_libs: shared_libraries, cflags: [ "-DLOG_TAG=\"dvr_api-test\"", "-DTRACE=0", + "-Wno-missing-field-initializers", "-O0", "-g", ], diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp index 16da1d9e54..62cd8d4e53 100644 --- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp +++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp @@ -1,28 +1,32 @@ +#include <android/log.h> +#include <android/native_window.h> +#include <android-base/unique_fd.h> #include <dvr/dvr_api.h> #include <dvr/dvr_buffer_queue.h> -#include <gui/Surface.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <base/logging.h> #include <gtest/gtest.h> -#include "../dvr_internal.h" -#include "../dvr_buffer_queue_internal.h" +#include <array> +#include <unordered_map> -namespace android { -namespace dvr { +#ifndef ALOGD +#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#endif + +#ifndef ALOGD_IF +#define ALOGD_IF(cond, ...) \ + ((__predict_false(cond)) ? ((void)ALOGD(__VA_ARGS__)) : (void)0) +#endif namespace { static constexpr uint32_t kBufferWidth = 100; static constexpr uint32_t kBufferHeight = 1; static constexpr uint32_t kLayerCount = 1; -static constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB; -static constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY; +static constexpr uint32_t kBufferFormat = AHARDWAREBUFFER_FORMAT_BLOB; +static constexpr uint64_t kBufferUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; static constexpr size_t kQueueCapacity = 3; -typedef uint64_t TestMeta; - class DvrBufferQueueTest : public ::testing::Test { public: static void BufferAvailableCallback(void* context) { @@ -36,14 +40,6 @@ class DvrBufferQueueTest : public ::testing::Test { } protected: - void SetUp() override { - config_builder_ = ProducerQueueConfigBuilder() - .SetDefaultWidth(kBufferWidth) - .SetDefaultHeight(kBufferHeight) - .SetDefaultFormat(kBufferFormat) - .SetMetadata<TestMeta>(); - } - void TearDown() override { if (write_queue_ != nullptr) { dvrWriteBufferQueueDestroy(write_queue_); @@ -51,19 +47,6 @@ class DvrBufferQueueTest : public ::testing::Test { } } - void CreateWriteBufferQueue() { - write_queue_ = new DvrWriteBufferQueue( - ProducerQueue::Create(config_builder_.Build(), UsagePolicy{})); - ASSERT_NE(nullptr, write_queue_); - } - - void AllocateBuffers(size_t buffer_count) { - auto status = write_queue_->producer_queue()->AllocateBuffers( - kBufferWidth, kBufferHeight, kLayerCount, kBufferFormat, kBufferUsage, - buffer_count); - ASSERT_TRUE(status.ok()); - } - void HandleBufferAvailable() { buffer_available_count_ += 1; ALOGD_IF(TRACE, "Buffer avaiable, count=%d", buffer_available_count_); @@ -75,22 +58,26 @@ class DvrBufferQueueTest : public ::testing::Test { buffer_removed_count_); } - ProducerQueueConfigBuilder config_builder_; DvrWriteBufferQueue* write_queue_{nullptr}; int buffer_available_count_{0}; int buffer_removed_count_{0}; }; -TEST_F(DvrBufferQueueTest, TestWrite_QueueCreateDestroy) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); +TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(0, ret); dvrWriteBufferQueueDestroy(write_queue_); write_queue_ = nullptr; } -TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); - ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity)); +TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(0, ret); size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_); @@ -98,11 +85,14 @@ TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) { ASSERT_EQ(kQueueCapacity, capacity); } -TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); +TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue = nullptr; - int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); + ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); @@ -110,12 +100,15 @@ TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) { dvrReadBufferQueueDestroy(read_queue); } -TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); +TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue1 = nullptr; DvrReadBufferQueue* read_queue2 = nullptr; - int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1); + ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue1); @@ -129,98 +122,86 @@ TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) { dvrReadBufferQueueDestroy(read_queue2); } -TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); - ASSERT_NO_FATAL_FAILURE(AllocateBuffers(3)); - - DvrReadBuffer* read_buffer = nullptr; - DvrWriteBuffer* write_buffer = nullptr; - - EXPECT_FALSE(dvrReadBufferIsValid(read_buffer)); - EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); - - dvrReadBufferCreateEmpty(&read_buffer); - ASSERT_NE(nullptr, read_buffer); - - dvrWriteBufferCreateEmpty(&write_buffer); - ASSERT_NE(nullptr, write_buffer); - - EXPECT_FALSE(dvrReadBufferIsValid(read_buffer)); - EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); - - DvrReadBufferQueue* read_queue = nullptr; +TEST_F(DvrBufferQueueTest, GainBuffer) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(ret, 0); - ASSERT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); + DvrWriteBuffer* wb = nullptr; + EXPECT_FALSE(dvrWriteBufferIsValid(wb)); - const int kTimeoutMs = 0; + DvrNativeBufferMetadata meta; int fence_fd = -1; - ASSERT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, kTimeoutMs, - write_buffer, &fence_fd)); - EXPECT_EQ(-1, fence_fd); - EXPECT_TRUE(dvrWriteBufferIsValid(write_buffer)); - - ASSERT_EQ(0, dvrWriteBufferClear(write_buffer)); - EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); + ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta, + &fence_fd); + ASSERT_EQ(ret, 0); + EXPECT_EQ(fence_fd, -1); + EXPECT_NE(wb, nullptr); + EXPECT_TRUE(dvrWriteBufferIsValid(wb)); } -TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); - ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity)); +TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(ret, 0); - static constexpr int kTimeout = 0; DvrReadBufferQueue* read_queue = nullptr; DvrReadBuffer* rb = nullptr; DvrWriteBuffer* wb = nullptr; + DvrNativeBufferMetadata meta1; + DvrNativeBufferMetadata meta2; int fence_fd = -1; - int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); + ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, read_queue); + ASSERT_EQ(ret, 0); + ASSERT_NE(read_queue, nullptr); dvrReadBufferQueueSetBufferAvailableCallback(read_queue, &BufferAvailableCallback, this); - dvrWriteBufferCreateEmpty(&wb); - ASSERT_NE(nullptr, wb); - - dvrReadBufferCreateEmpty(&rb); - ASSERT_NE(nullptr, rb); - // Gain buffer for writing. - ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb, &fence_fd); - ASSERT_EQ(0, ret); + ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta1, + &fence_fd); + ASSERT_EQ(ret, 0); + ASSERT_NE(wb, nullptr); ASSERT_TRUE(dvrWriteBufferIsValid(wb)); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d", wb, fence_fd); - pdx::LocalHandle release_fence(fence_fd); + android::base::unique_fd release_fence(fence_fd); // Post buffer to the read_queue. - TestMeta seq = 42U; - ret = dvrWriteBufferPost(wb, /* fence */ -1, &seq, sizeof(seq)); - ASSERT_EQ(0, ret); - dvrWriteBufferDestroy(wb); + meta1.timestamp = 42; + ret = dvrWriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1); + ASSERT_EQ(ret, 0); + ASSERT_FALSE(dvrWriteBufferIsValid(wb)); wb = nullptr; // Acquire buffer for reading. - TestMeta acquired_seq = 0U; - ret = dvrReadBufferQueueDequeue(read_queue, kTimeout, rb, &fence_fd, - &acquired_seq, sizeof(acquired_seq)); - ASSERT_EQ(0, ret); + ret = dvrReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb, &meta2, + &fence_fd); + ASSERT_EQ(ret, 0); + ASSERT_NE(rb, nullptr); // Dequeue is successfully, BufferAvailableCallback should be fired once. - ASSERT_EQ(1, buffer_available_count_); + ASSERT_EQ(buffer_available_count_, 1); ASSERT_TRUE(dvrReadBufferIsValid(rb)); - ASSERT_EQ(seq, acquired_seq); + + // Metadata should be passed along from producer to consumer properly. + ASSERT_EQ(meta1.timestamp, meta2.timestamp); + ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb, fence_fd); - pdx::LocalHandle acquire_fence(fence_fd); + android::base::unique_fd acquire_fence(fence_fd); // Release buffer to the write_queue. - ret = dvrReadBufferRelease(rb, -1); - ASSERT_EQ(0, ret); - dvrReadBufferDestroy(rb); + ret = dvrReadBufferQueueReleaseBuffer(read_queue, rb, &meta2, + /*release_fence_fd=*/-1); + ASSERT_EQ(ret, 0); + ASSERT_FALSE(dvrReadBufferIsValid(rb)); rb = nullptr; // TODO(b/34387835) Currently buffer allocation has to happen after all queues @@ -233,45 +214,38 @@ TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) { dvrReadBufferQueueDestroy(read_queue); } -TEST_F(DvrBufferQueueTest, TestGetExternalSurface) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); +TEST_F(DvrBufferQueueTest, GetANativeWindow) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(0, ret); + ASSERT_NE(nullptr, write_queue_); ANativeWindow* window = nullptr; - - // The |write_queue_| doesn't have proper metadata (must be - // DvrNativeBufferMetadata) configured during creation. - int ret = dvrWriteBufferQueueGetExternalSurface(write_queue_, &window); - ASSERT_EQ(-EINVAL, ret); - ASSERT_EQ(nullptr, window); - - // A write queue with DvrNativeBufferMetadata should work fine. - auto config = ProducerQueueConfigBuilder() - .SetMetadata<DvrNativeBufferMetadata>() - .Build(); - std::unique_ptr<DvrWriteBufferQueue, decltype(&dvrWriteBufferQueueDestroy)> - write_queue( - new DvrWriteBufferQueue(ProducerQueue::Create(config, UsagePolicy{})), - dvrWriteBufferQueueDestroy); - ASSERT_NE(nullptr, write_queue.get()); - - ret = dvrWriteBufferQueueGetExternalSurface(write_queue.get(), &window); + ret = dvrWriteBufferQueueGetANativeWindow(write_queue_, &window); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, window); - sp<Surface> surface = static_cast<Surface*>(window); - ASSERT_TRUE(Surface::isValid(surface)); + uint32_t width = ANativeWindow_getWidth(window); + uint32_t height = ANativeWindow_getHeight(window); + uint32_t format = ANativeWindow_getFormat(window); + ASSERT_EQ(kBufferWidth, width); + ASSERT_EQ(kBufferHeight, height); + ASSERT_EQ(kBufferFormat, format); } // Create buffer queue of three buffers and dequeue three buffers out of it. // Before each dequeue operation, we resize the buffer queue and expect the // queue always return buffer with desired dimension. -TEST_F(DvrBufferQueueTest, TestResizeBuffer) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); - ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity)); +TEST_F(DvrBufferQueueTest, ResizeBuffer) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(0, ret); - static constexpr int kTimeout = 0; int fence_fd = -1; + DvrNativeBufferMetadata meta; DvrReadBufferQueue* read_queue = nullptr; DvrWriteBuffer* wb1 = nullptr; DvrWriteBuffer* wb2 = nullptr; @@ -281,7 +255,7 @@ TEST_F(DvrBufferQueueTest, TestResizeBuffer) { AHardwareBuffer* ahb3 = nullptr; AHardwareBuffer_Desc buffer_desc; - int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); + ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); @@ -289,13 +263,6 @@ TEST_F(DvrBufferQueueTest, TestResizeBuffer) { dvrReadBufferQueueSetBufferRemovedCallback(read_queue, &BufferRemovedCallback, this); - dvrWriteBufferCreateEmpty(&wb1); - ASSERT_NE(nullptr, wb1); - dvrWriteBufferCreateEmpty(&wb2); - ASSERT_NE(nullptr, wb2); - dvrWriteBufferCreateEmpty(&wb3); - ASSERT_NE(nullptr, wb3); - // Handle all pending events on the read queue. ret = dvrReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); @@ -310,11 +277,12 @@ TEST_F(DvrBufferQueueTest, TestResizeBuffer) { ASSERT_EQ(0, ret); // Gain first buffer for writing. All buffers will be resized. - ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb1, &fence_fd); + ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1, &meta, + &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(dvrWriteBufferIsValid(wb1)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1); - pdx::LocalHandle release_fence1(fence_fd); + android::base::unique_fd release_fence1(fence_fd); // Check the buffer dimension. ret = dvrWriteBufferGetAHardwareBuffer(wb1, &ahb1); @@ -336,12 +304,13 @@ TEST_F(DvrBufferQueueTest, TestResizeBuffer) { ASSERT_EQ(0, ret); // The next buffer we dequeued should have new width. - ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb2, &fence_fd); + ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2, &meta, + &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(dvrWriteBufferIsValid(wb2)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2, fence_fd); - pdx::LocalHandle release_fence2(fence_fd); + android::base::unique_fd release_fence2(fence_fd); // Check the buffer dimension, should be new width ret = dvrWriteBufferGetAHardwareBuffer(wb2, &ahb2); @@ -362,12 +331,13 @@ TEST_F(DvrBufferQueueTest, TestResizeBuffer) { ASSERT_EQ(0, ret); // The next buffer we dequeued should have new width. - ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb3, &fence_fd); + ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3, &meta, + &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(dvrWriteBufferIsValid(wb3)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3, fence_fd); - pdx::LocalHandle release_fence3(fence_fd); + android::base::unique_fd release_fence3(fence_fd); // Check the buffer dimension, should be new width ret = dvrWriteBufferGetAHardwareBuffer(wb3, &ahb3); @@ -385,86 +355,170 @@ TEST_F(DvrBufferQueueTest, TestResizeBuffer) { dvrReadBufferQueueDestroy(read_queue); } -TEST_F(DvrBufferQueueTest, DequeueEmptyMetadata) { - // Overrides default queue parameters: Empty metadata. - config_builder_.SetMetadata<void>(); - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); - ASSERT_NO_FATAL_FAILURE(AllocateBuffers(1)); - - DvrReadBuffer* rb = nullptr; - DvrWriteBuffer* wb = nullptr; - dvrReadBufferCreateEmpty(&rb); - dvrWriteBufferCreateEmpty(&wb); +TEST_F(DvrBufferQueueTest, ReadQueueEventFd) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue = nullptr; - EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); + ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); - const int kTimeoutMs = 0; - int fence_fd = -1; - EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd)); - - EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, nullptr, 0)); - EXPECT_EQ(0, dvrWriteBufferClear(wb)); - dvrWriteBufferDestroy(wb); - wb = nullptr; + ASSERT_EQ(0, ret); + ASSERT_NE(nullptr, read_queue); - // When acquire buffer, it's legit to pass nullptr as out_meta iff metadata - // size is Zero. - EXPECT_EQ(0, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd, - nullptr, 0)); - EXPECT_TRUE(dvrReadBufferIsValid(rb)); + int event_fd = dvrReadBufferQueueGetEventFd(read_queue); + ASSERT_GT(event_fd, 0); } -TEST_F(DvrBufferQueueTest, DequeueMismatchMetadata) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); - ASSERT_NO_FATAL_FAILURE(AllocateBuffers(1)); - - DvrReadBuffer* rb = nullptr; - DvrWriteBuffer* wb = nullptr; - dvrReadBufferCreateEmpty(&rb); - dvrWriteBufferCreateEmpty(&wb); +// Verifies a Dvr{Read,Write}BufferQueue contains the same set of +// Dvr{Read,Write}Buffer(s) during their lifecycles. And for the same buffer_id, +// the corresponding AHardwareBuffer handle stays the same. +TEST_F(DvrBufferQueueTest, StableBufferIdAndHardwareBuffer) { + int ret = dvrWriteBufferQueueCreate( + kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, + kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); + ASSERT_EQ(0, ret); + int fence_fd = -1; DvrReadBufferQueue* read_queue = nullptr; EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); - const int kTimeoutMs = 0; - int fence_fd = -1; - EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd)); + // Read buffers. + std::array<DvrReadBuffer*, kQueueCapacity> rbs; + // Write buffers. + std::array<DvrWriteBuffer*, kQueueCapacity> wbs; + // Buffer metadata. + std::array<DvrNativeBufferMetadata, kQueueCapacity> metas; + // Hardware buffers for Read buffers. + std::unordered_map<int, AHardwareBuffer*> rhbs; + // Hardware buffers for Write buffers. + std::unordered_map<int, AHardwareBuffer*> whbs; + + constexpr int kNumTests = 100; + + // This test runs the following operations many many times. Thus we prefer to + // use ASSERT_XXX rather than EXPECT_XXX to avoid spamming the output. + std::function<void(size_t i)> Gain = [&](size_t i) { + int ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/10, + &wbs[i], &metas[i], &fence_fd); + ASSERT_EQ(ret, 0); + ASSERT_LT(fence_fd, 0); // expect invalid fence. + ASSERT_TRUE(dvrWriteBufferIsValid(wbs[i])); + int buffer_id = dvrWriteBufferGetId(wbs[i]); + ASSERT_GT(buffer_id, 0); + + AHardwareBuffer* hb = nullptr; + ASSERT_EQ(0, dvrWriteBufferGetAHardwareBuffer(wbs[i], &hb)); + + auto whb_it = whbs.find(buffer_id); + if (whb_it == whbs.end()) { + // If this is a new buffer id, check that total number of unique + // hardware buffers won't exceed queue capacity. + ASSERT_LT(whbs.size(), kQueueCapacity); + whbs.emplace(buffer_id, hb); + } else { + // If this is a buffer id we have seen before, check that the + // buffer_id maps to the same AHardwareBuffer handle. + ASSERT_EQ(hb, whb_it->second); + } + }; + + std::function<void(size_t i)> Post = [&](size_t i) { + ASSERT_TRUE(dvrWriteBufferIsValid(wbs[i])); + + metas[i].timestamp++; + int ret = dvrWriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i], + /*fence=*/-1); + ASSERT_EQ(ret, 0); + }; + + std::function<void(size_t i)> Acquire = [&](size_t i) { + int ret = dvrReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, + &rbs[i], &metas[i], &fence_fd); + ASSERT_EQ(ret, 0); + ASSERT_LT(fence_fd, 0); // expect invalid fence. + ASSERT_TRUE(dvrReadBufferIsValid(rbs[i])); + + int buffer_id = dvrReadBufferGetId(rbs[i]); + ASSERT_GT(buffer_id, 0); + + AHardwareBuffer* hb = nullptr; + ASSERT_EQ(0, dvrReadBufferGetAHardwareBuffer(rbs[i], &hb)); + + auto rhb_it = rhbs.find(buffer_id); + if (rhb_it == rhbs.end()) { + // If this is a new buffer id, check that total number of unique hardware + // buffers won't exceed queue capacity. + ASSERT_LT(rhbs.size(), kQueueCapacity); + rhbs.emplace(buffer_id, hb); + } else { + // If this is a buffer id we have seen before, check that the buffer_id + // maps to the same AHardwareBuffer handle. + ASSERT_EQ(hb, rhb_it->second); + } + }; - TestMeta seq = 42U; - EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, &seq, sizeof(seq))); - EXPECT_EQ(0, dvrWriteBufferClear(wb)); - dvrWriteBufferDestroy(wb); - wb = nullptr; + std::function<void(size_t i)> Release = [&](size_t i) { + ASSERT_TRUE(dvrReadBufferIsValid(rbs[i])); - // Dequeue with wrong metadata will cause EINVAL. - int8_t wrong_metadata; - EXPECT_EQ(-EINVAL, - dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd, - &wrong_metadata, sizeof(wrong_metadata))); - EXPECT_FALSE(dvrReadBufferIsValid(rb)); - - // Dequeue with empty metadata will cause EINVAL. - EXPECT_EQ(-EINVAL, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, - &fence_fd, nullptr, 0)); - EXPECT_FALSE(dvrReadBufferIsValid(rb)); -} + int ret = dvrReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i], + /*release_fence_fd=*/-1); + ASSERT_EQ(ret, 0); + }; -TEST_F(DvrBufferQueueTest, TestReadQueueEventFd) { - ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); - ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity)); + // Scenario one: + for (int i = 0; i < kNumTests; i++) { + // Gain all write buffers. + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Gain(i)); + } + // Post all write buffers. + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Post(i)); + } + // Acquire all read buffers. + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Acquire(i)); + } + // Release all read buffers. + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Release(i)); + } + } - DvrReadBufferQueue* read_queue = nullptr; - int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); + // Scenario two: + for (int i = 0; i < kNumTests; i++) { + // Gain and post all write buffers. + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Gain(i)); + ASSERT_NO_FATAL_FAILURE(Post(i)); + } + // Acquire and release all read buffers. + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Acquire(i)); + ASSERT_NO_FATAL_FAILURE(Release(i)); + } + } - ASSERT_EQ(0, ret); - ASSERT_NE(nullptr, read_queue); + // Scenario three: + for (int i = 0; i < kNumTests; i++) { + // Gain all write buffers then post them in reversed order. + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Gain(i)); + } + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Post(kQueueCapacity - 1 - i)); + } - int event_fd = dvrReadBufferQueueGetEventFd(read_queue); - ASSERT_GT(event_fd, 0); + // Acquire all write buffers then release them in reversed order. + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Acquire(i)); + } + for (size_t i = 0; i < kQueueCapacity; i++) { + ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i)); + } + } } } // namespace - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp index f55e9942db..10c0b31c57 100644 --- a/libs/vr/libpdx/Android.bp +++ b/libs/vr/libpdx/Android.bp @@ -5,12 +5,15 @@ cc_library_static { "-Wall", "-Wextra", "-Werror", + "-DLOG_TAG=\"libpdx\"", + "-DTRACE=0", ], export_include_dirs: ["private"], local_include_dirs: ["private"], srcs: [ "client.cpp", "service.cpp", + "service_dispatcher.cpp", "status.cpp", ], } @@ -33,6 +36,7 @@ cc_test { "variant_tests.cpp", ], static_libs: [ + "libcutils", "libgmock", "libpdx", "liblog", diff --git a/libs/vr/libpdx/client.cpp b/libs/vr/libpdx/client.cpp index bfa2d879b5..a01c4d67ec 100644 --- a/libs/vr/libpdx/client.cpp +++ b/libs/vr/libpdx/client.cpp @@ -1,6 +1,5 @@ #include "pdx/client.h" -#define LOG_TAG "ServiceFramework" #include <log/log.h> #include <pdx/trace.h> diff --git a/libs/vr/libpdx/mock_tests.cpp b/libs/vr/libpdx/mock_tests.cpp index 76fd1541a5..4143837cb4 100644 --- a/libs/vr/libpdx/mock_tests.cpp +++ b/libs/vr/libpdx/mock_tests.cpp @@ -3,7 +3,6 @@ #include <pdx/mock_client_channel_factory.h> #include <pdx/mock_message_reader.h> #include <pdx/mock_message_writer.h> -#include <pdx/mock_service_dispatcher.h> #include <pdx/mock_service_endpoint.h> TEST(MockTypes, Instantiation) { @@ -15,6 +14,5 @@ TEST(MockTypes, Instantiation) { android::pdx::MockMessageReader message_reader; android::pdx::MockOutputResourceMapper output_resource_mapper; android::pdx::MockMessageWriter message_writer; - android::pdx::MockServiceDispatcher service_dispatcher; android::pdx::MockEndpoint endpoint; } diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h index dbfd626d6f..10a49bb8d7 100644 --- a/libs/vr/libpdx/private/pdx/client_channel.h +++ b/libs/vr/libpdx/private/pdx/client_channel.h @@ -1,6 +1,8 @@ #ifndef ANDROID_PDX_CLIENT_CHANNEL_H_ #define ANDROID_PDX_CLIENT_CHANNEL_H_ +#include <vector> + #include <pdx/channel_handle.h> #include <pdx/file_handle.h> #include <pdx/status.h> @@ -20,6 +22,15 @@ class ClientChannel { virtual int event_fd() const = 0; virtual Status<int> GetEventMask(int events) = 0; + struct EventSource { + int event_fd; + int event_mask; + }; + + // Returns a set of event-generating fds with and event mask for each. These + // fds are owned by the ClientChannel and must never be closed by the caller. + virtual std::vector<EventSource> GetEventSources() const = 0; + virtual LocalChannelHandle& GetChannelHandle() = 0; virtual void* AllocateTransactionState() = 0; virtual void FreeTransactionState(void* state) = 0; diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h index 561c939daf..49e0682bc9 100644 --- a/libs/vr/libpdx/private/pdx/mock_client_channel.h +++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h @@ -11,6 +11,7 @@ class MockClientChannel : public ClientChannel { public: MOCK_CONST_METHOD0(GetIpcTag, uint32_t()); MOCK_CONST_METHOD0(event_fd, int()); + MOCK_CONST_METHOD0(GetEventSources, std::vector<EventSource>()); MOCK_METHOD1(GetEventMask, Status<int>(int)); MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&()); MOCK_METHOD0(AllocateTransactionState, void*()); diff --git a/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h deleted file mode 100644 index 9b51d30592..0000000000 --- a/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_ -#define ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_ - -#include <gmock/gmock.h> -#include <pdx/service_dispatcher.h> - -namespace android { -namespace pdx { - -class MockServiceDispatcher : public ServiceDispatcher { - public: - MOCK_METHOD1(AddService, int(const std::shared_ptr<Service>& service)); - MOCK_METHOD1(RemoveService, int(const std::shared_ptr<Service>& service)); - MOCK_METHOD0(ReceiveAndDispatch, int()); - MOCK_METHOD1(ReceiveAndDispatch, int(int timeout)); - MOCK_METHOD0(EnterDispatchLoop, int()); - MOCK_METHOD1(SetCanceled, void(bool cancel)); - MOCK_CONST_METHOD0(IsCanceled, bool()); -}; - -} // namespace pdx -} // namespace android - -#endif // ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx/private/pdx/mock_service_endpoint.h b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h index e741d4a46b..7f829e70d0 100644 --- a/libs/vr/libpdx/private/pdx/mock_service_endpoint.h +++ b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h @@ -66,6 +66,7 @@ class MockEndpoint : public Endpoint { MOCK_METHOD0(AllocateMessageState, void*()); MOCK_METHOD1(FreeMessageState, void(void* state)); MOCK_METHOD0(Cancel, Status<void>()); + MOCK_CONST_METHOD0(epoll_fd, int()); }; } // namespace pdx diff --git a/libs/vr/libpdx/private/pdx/service_dispatcher.h b/libs/vr/libpdx/private/pdx/service_dispatcher.h index c5e342af0c..bd27000dc9 100644 --- a/libs/vr/libpdx/private/pdx/service_dispatcher.h +++ b/libs/vr/libpdx/private/pdx/service_dispatcher.h @@ -2,6 +2,11 @@ #define ANDROID_PDX_SERVICE_DISPATCHER_H_ #include <memory> +#include <mutex> +#include <unordered_map> +#include <vector> + +#include <pdx/file_handle.h> namespace android { namespace pdx { @@ -15,7 +20,10 @@ class Service; */ class ServiceDispatcher { public: - virtual ~ServiceDispatcher() = default; + // Get a new instance of ServiceDispatcher, or return nullptr if init failed. + static std::unique_ptr<ServiceDispatcher> Create(); + + ~ServiceDispatcher(); /* * Adds a service to the list of services handled by this dispatcher. This @@ -24,7 +32,7 @@ class ServiceDispatcher { * * Returns 0 on success; -EEXIST if the service was already added. */ - virtual int AddService(const std::shared_ptr<Service>& service) = 0; + int AddService(const std::shared_ptr<Service>& service); /* * Removes a service from this dispatcher. This will fail if any threads are @@ -33,7 +41,7 @@ class ServiceDispatcher { * Returns 0 on success; -ENOENT if the service was not previously added; * -EBUSY if there are threads in the dispatcher. */ - virtual int RemoveService(const std::shared_ptr<Service>& service) = 0; + int RemoveService(const std::shared_ptr<Service>& service); /* * Receive and dispatch one set of messages. Multiple threads may enter this @@ -42,14 +50,14 @@ class ServiceDispatcher { * cycle, requiring an external loop. This is useful when other work needs * to be done in the service dispatch loop. */ - virtual int ReceiveAndDispatch() = 0; + int ReceiveAndDispatch(); /* * Same as above with timeout in milliseconds. A negative value means * infinite timeout, while a value of 0 means return immediately if no * messages are available to receive. */ - virtual int ReceiveAndDispatch(int timeout) = 0; + int ReceiveAndDispatch(int timeout); /* * Receive and dispatch messages until canceled. When more than one thread @@ -58,19 +66,39 @@ class ServiceDispatcher { * hands Message instances (via move assignment) over to a queue of threads * (or perhaps one of several) to handle. */ - virtual int EnterDispatchLoop() = 0; + int EnterDispatchLoop(); /* * Sets the canceled state of the dispatcher. When canceled is true, any * threads blocked waiting for messages will return. This method waits until * all dispatch threads have exited the dispatcher. */ - virtual void SetCanceled(bool cancel) = 0; + void SetCanceled(bool cancel); /* * Gets the canceled state of the dispatcher. */ - virtual bool IsCanceled() const = 0; + bool IsCanceled() const; + + private: + ServiceDispatcher(); + + // Internal thread accounting. + int ThreadEnter(); + void ThreadExit(); + + std::mutex mutex_; + std::condition_variable condition_; + std::atomic<bool> canceled_{false}; + + std::vector<std::shared_ptr<Service>> services_; + + int thread_count_ = 0; + LocalHandle event_fd_; + LocalHandle epoll_fd_; + + ServiceDispatcher(const ServiceDispatcher&) = delete; + void operator=(const ServiceDispatcher&) = delete; }; } // namespace pdx diff --git a/libs/vr/libpdx/private/pdx/service_endpoint.h b/libs/vr/libpdx/private/pdx/service_endpoint.h index 28bd6bc454..d58189499c 100644 --- a/libs/vr/libpdx/private/pdx/service_endpoint.h +++ b/libs/vr/libpdx/private/pdx/service_endpoint.h @@ -136,6 +136,10 @@ class Endpoint { // Cancels the endpoint, unblocking any receiver threads waiting for a // message. virtual Status<void> Cancel() = 0; + + // Returns an fd that can be used with epoll() to wait for incoming messages + // from this endpoint. + virtual int epoll_fd() const = 0; }; } // namespace pdx diff --git a/libs/vr/libpdx/private/pdx/trace.h b/libs/vr/libpdx/private/pdx/trace.h index ebe8491ebc..c687fd6259 100644 --- a/libs/vr/libpdx/private/pdx/trace.h +++ b/libs/vr/libpdx/private/pdx/trace.h @@ -1,35 +1,82 @@ #ifndef ANDROID_PDX_TRACE_H_ #define ANDROID_PDX_TRACE_H_ -// Tracing utilities for libpdx. Tracing in the service framework is enabled -// under these conditions: -// 1. ATRACE_TAG is defined, AND -// 2. ATRACE_TAG does not equal ATRACE_TAG_NEVER, AND -// 3. PDX_TRACE_ENABLED is defined, AND -// 4. PDX_TRACE_ENABLED is equal to logical true. -// -// If any of these conditions are not met tracing is completely removed from the -// library and headers. - -// If ATRACE_TAG is not defined, default to never. -#ifndef ATRACE_TAG -#define ATRACE_TAG ATRACE_TAG_NEVER -#endif +#include <array> -// Include tracing functions after the trace tag is defined. #include <utils/Trace.h> -// If PDX_TRACE_ENABLED is not defined, default to off. -#ifndef PDX_TRACE_ENABLED -#define PDX_TRACE_ENABLED 0 +// Enables internal tracing in libpdx. This is disabled by default to avoid +// spamming the trace buffers during normal trace activities. libpdx must be +// built with this set to true to enable internal tracing. +#ifndef PDX_LIB_TRACE_ENABLED +#define PDX_LIB_TRACE_ENABLED false #endif -#if (ATRACE_TAG) != (ATRACE_TAG_NEVER) && (PDX_TRACE_ENABLED) -#define PDX_TRACE_NAME ATRACE_NAME -#else -#define PDX_TRACE_NAME(name) \ - do { \ - } while (0) -#endif +namespace android { +namespace pdx { + +// Utility to generate scoped tracers with arguments. +class ScopedTraceArgs { + public: + template <typename... Args> + ScopedTraceArgs(uint64_t tag, const char* format, Args&&... args) + : tag_{tag} { + if (atrace_is_tag_enabled(tag_)) { + std::array<char, 1024> buffer; + snprintf(buffer.data(), buffer.size(), format, + std::forward<Args>(args)...); + atrace_begin(tag_, buffer.data()); + } + } + + ~ScopedTraceArgs() { atrace_end(tag_); } + + private: + uint64_t tag_; + + ScopedTraceArgs(const ScopedTraceArgs&) = delete; + void operator=(const ScopedTraceArgs&) = delete; +}; + +// Utility to generate scoped tracers. +class ScopedTrace { + public: + template <typename... Args> + ScopedTrace(uint64_t tag, bool enabled, const char* name) + : tag_{tag}, enabled_{enabled} { + if (enabled_) + atrace_begin(tag_, name); + } + + ~ScopedTrace() { + if (enabled_) + atrace_end(tag_); + } + + private: + uint64_t tag_; + bool enabled_; + + ScopedTrace(const ScopedTrace&) = delete; + void operator=(const ScopedTrace&) = delete; +}; + +} // namespace pdx +} // namespace android + +// Macro to define a scoped tracer with arguments. Uses PASTE(x, y) macro +// defined in utils/Trace.h. +#define PDX_TRACE_FORMAT(format, ...) \ + ::android::pdx::ScopedTraceArgs PASTE(__tracer, __LINE__) { \ + ATRACE_TAG, format, ##__VA_ARGS__ \ + } + +// TODO(eieio): Rename this to PDX_LIB_TRACE_NAME() for internal use by libpdx +// and rename internal uses inside the library. This version is only enabled +// when PDX_LIB_TRACE_ENABLED is true. +#define PDX_TRACE_NAME(name) \ + ::android::pdx::ScopedTrace PASTE(__tracer, __LINE__) { \ + ATRACE_TAG, PDX_LIB_TRACE_ENABLED, name \ + } #endif // ANDROID_PDX_TRACE_H_ diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp index fab4770b7f..1d3b62ac08 100644 --- a/libs/vr/libpdx/service.cpp +++ b/libs/vr/libpdx/service.cpp @@ -1,4 +1,3 @@ -#define LOG_TAG "ServiceFramework" #include "pdx/service.h" #include <fcntl.h> @@ -10,8 +9,6 @@ #include <pdx/trace.h> -#define TRACE 0 - namespace android { namespace pdx { diff --git a/libs/vr/libpdx_uds/service_dispatcher.cpp b/libs/vr/libpdx/service_dispatcher.cpp index 2c52578d1c..b112fa30eb 100644 --- a/libs/vr/libpdx_uds/service_dispatcher.cpp +++ b/libs/vr/libpdx/service_dispatcher.cpp @@ -1,26 +1,25 @@ -#include "uds/service_dispatcher.h" +#include <pdx/service_dispatcher.h> #include <errno.h> #include <log/log.h> #include <sys/epoll.h> #include <sys/eventfd.h> -#include "pdx/service.h" -#include "uds/service_endpoint.h" +#include <pdx/service.h> +#include <pdx/service_endpoint.h> static const int kMaxEventsPerLoop = 128; namespace android { namespace pdx { -namespace uds { -std::unique_ptr<pdx::ServiceDispatcher> ServiceDispatcher::Create() { +std::unique_ptr<ServiceDispatcher> ServiceDispatcher::Create() { std::unique_ptr<ServiceDispatcher> dispatcher{new ServiceDispatcher()}; if (!dispatcher->epoll_fd_ || !dispatcher->event_fd_) { dispatcher.reset(); } - return std::move(dispatcher); + return dispatcher; } ServiceDispatcher::ServiceDispatcher() { @@ -70,18 +69,14 @@ void ServiceDispatcher::ThreadExit() { } int ServiceDispatcher::AddService(const std::shared_ptr<Service>& service) { - if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag) - return -EINVAL; - std::lock_guard<std::mutex> autolock(mutex_); - auto* endpoint = static_cast<Endpoint*>(service->endpoint()); epoll_event event; event.events = EPOLLIN; event.data.ptr = service.get(); - if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, endpoint->epoll_fd(), &event) < - 0) { + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, service->endpoint()->epoll_fd(), + &event) < 0) { ALOGE("Failed to add service to dispatcher because: %s\n", strerror(errno)); return -errno; } @@ -91,9 +86,6 @@ int ServiceDispatcher::AddService(const std::shared_ptr<Service>& service) { } int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) { - if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag) - return -EINVAL; - std::lock_guard<std::mutex> autolock(mutex_); // It's dangerous to remove a service while other threads may be using it. @@ -101,16 +93,15 @@ int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) { return -EBUSY; epoll_event dummy; // See BUGS in man 2 epoll_ctl. - - auto* endpoint = static_cast<Endpoint*>(service->endpoint()); - if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, endpoint->epoll_fd(), &dummy) < - 0) { + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, service->endpoint()->epoll_fd(), + &dummy) < 0) { ALOGE("Failed to remove service from dispatcher because: %s\n", strerror(errno)); return -errno; } - services_.remove(service); + services_.erase(std::remove(services_.begin(), services_.end(), service), + services_.end()); return 0; } @@ -139,7 +130,7 @@ int ServiceDispatcher::ReceiveAndDispatch(int timeout) { Service* service = static_cast<Service*>(events[i].data.ptr); ALOGI_IF(TRACE, "Dispatching message: fd=%d\n", - static_cast<Endpoint*>(service->endpoint())->epoll_fd()); + service->endpoint()->epoll_fd()); service->ReceiveAndDispatch(); } } @@ -171,7 +162,7 @@ int ServiceDispatcher::EnterDispatchLoop() { Service* service = static_cast<Service*>(events[i].data.ptr); ALOGI_IF(TRACE, "Dispatching message: fd=%d\n", - static_cast<Endpoint*>(service->endpoint())->epoll_fd()); + service->endpoint()->epoll_fd()); service->ReceiveAndDispatch(); } } @@ -197,6 +188,5 @@ void ServiceDispatcher::SetCanceled(bool cancel) { bool ServiceDispatcher::IsCanceled() const { return canceled_; } -} // namespace uds } // namespace pdx } // namespace android diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp index 8cfa86fa44..f891c59fde 100644 --- a/libs/vr/libpdx_default_transport/Android.bp +++ b/libs/vr/libpdx_default_transport/Android.bp @@ -36,12 +36,13 @@ cc_library_static { } cc_binary { - name: "servicetool", + name: "pdx_tool", defaults: ["pdx_default_transport_compiler_defaults"], srcs: [ - "servicetool.cpp", + "pdx_tool.cpp", ], shared_libs: [ + "libcutils", "liblog", ], static_libs: [ diff --git a/libs/vr/libpdx_default_transport/servicetool.cpp b/libs/vr/libpdx_default_transport/pdx_tool.cpp index 60eedb3847..60eedb3847 100644 --- a/libs/vr/libpdx_default_transport/servicetool.cpp +++ b/libs/vr/libpdx_default_transport/pdx_tool.cpp diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h deleted file mode 100644 index 158871c892..0000000000 --- a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_ -#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_ - -#include <servicefs/service_dispatcher.h> - -namespace android { -namespace pdx { -namespace default_transport { - -using ServiceDispatcher = ::android::pdx::servicefs::ServiceDispatcher; - -} // namespace default_transport -} // namespace pdx -} // namespace android - - -#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h deleted file mode 100644 index 7cb7a80fe7..0000000000 --- a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_ -#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_ - -#include <uds/service_dispatcher.h> - -namespace android { -namespace pdx { -namespace default_transport { - -using ServiceDispatcher = ::android::pdx::uds::ServiceDispatcher; - -} // namespace default_transport -} // namespace pdx -} // namespace android - - -#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp index 82a5ea7526..d0b7cab34f 100644 --- a/libs/vr/libpdx_uds/Android.bp +++ b/libs/vr/libpdx_uds/Android.bp @@ -16,7 +16,6 @@ cc_library_static { "client_channel_factory.cpp", "client_channel.cpp", "ipc_helper.cpp", - "service_dispatcher.cpp", "service_endpoint.cpp", ], static_libs: [ diff --git a/libs/vr/libpdx_uds/channel_event_set.cpp b/libs/vr/libpdx_uds/channel_event_set.cpp index ebe7cea7e3..c68968e1f2 100644 --- a/libs/vr/libpdx_uds/channel_event_set.cpp +++ b/libs/vr/libpdx_uds/channel_event_set.cpp @@ -1,6 +1,10 @@ #include "private/uds/channel_event_set.h" +#include <errno.h> #include <log/log.h> +#include <poll.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> #include <uds/ipc_helper.h> @@ -8,109 +12,137 @@ namespace android { namespace pdx { namespace uds { +namespace { + +template <typename FileHandleType> +Status<void> SetupHandle(int fd, FileHandleType* handle, + const char* error_name) { + const int error = errno; + handle->Reset(fd); + if (!*handle) { + ALOGE("SetupHandle: Failed to setup %s handle: %s", error_name, + strerror(error)); + return ErrorStatus{error}; + } + return {}; +} + +} // anonymous namespace + ChannelEventSet::ChannelEventSet() { const int flags = EFD_CLOEXEC | EFD_NONBLOCK; - LocalHandle epoll_fd, event_fd; + LocalHandle pollin_event_fd, pollhup_event_fd; - if (!SetupHandle(epoll_create1(EPOLL_CLOEXEC), &epoll_fd, "epoll") || - !SetupHandle(eventfd(0, flags), &event_fd, "event")) { + if (!SetupHandle(eventfd(0, flags), &pollin_event_fd, "pollin_event") || + !SetupHandle(eventfd(0, flags), &pollhup_event_fd, "pollhup_event")) { + return; + } + + pollin_event_fd_ = std::move(pollin_event_fd); + pollhup_event_fd_ = std::move(pollhup_event_fd); +} + +int ChannelEventSet::ModifyEvents(int clear_mask, int set_mask) { + ALOGD_IF(TRACE, "ChannelEventSet::ModifyEvents: clear_mask=%x set_mask=%x", + clear_mask, set_mask); + const int old_bits = event_bits_; + const int new_bits = (event_bits_ & ~clear_mask) | set_mask; + event_bits_ = new_bits; + eventfd_t value; + + // Calculate which bits changed and how. Bits that haven't changed since last + // modification will not change the state of an eventfd. + const int set_bits = new_bits & ~old_bits; + const int clear_bits = ~new_bits & old_bits; + + if (set_bits & EPOLLIN) + eventfd_write(pollin_event_fd_.Get(), 1); + else if (clear_bits & EPOLLIN) + eventfd_read(pollin_event_fd_.Get(), &value); + + if (set_bits & EPOLLHUP) + eventfd_write(pollhup_event_fd_.Get(), 1); + else if (clear_bits & EPOLLHUP) + eventfd_read(pollhup_event_fd_.Get(), &value); + + return 0; +} + +ChannelEventReceiver::ChannelEventReceiver(LocalHandle data_fd, + LocalHandle pollin_event_fd, + LocalHandle pollhup_event_fd) { + LocalHandle epoll_fd; + if (!SetupHandle(epoll_create1(EPOLL_CLOEXEC), &epoll_fd, "epoll")) { return; } epoll_event event; - event.events = 0; + event.events = EPOLLHUP | EPOLLRDHUP; event.data.u32 = 0; - if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_fd.Get(), &event) < 0) { + if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, data_fd.Get(), &event) < 0) { const int error = errno; - ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s", + ALOGE("ChannelEventSet::ChannelEventSet: Failed to add data_fd: %s", strerror(error)); return; } - epoll_fd_ = std::move(epoll_fd); - event_fd_ = std::move(event_fd); -} - -Status<void> ChannelEventSet::AddDataFd(const LocalHandle& data_fd) { - epoll_event event; - event.events = EPOLLHUP | EPOLLRDHUP; - event.data.u32 = event.events; - if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, data_fd.Get(), &event) < 0) { + event.events = EPOLLIN; + event.data.u32 = 0; + if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, pollin_event_fd.Get(), &event) < + 0) { const int error = errno; - ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s", + ALOGE("ChannelEventSet::ChannelEventSet: Failed to add pollin_event_fd: %s", strerror(error)); - return ErrorStatus{error}; - } else { - return {}; + return; } -} -int ChannelEventSet::ModifyEvents(int clear_mask, int set_mask) { - ALOGD_IF(TRACE, "ChannelEventSet::ModifyEvents: clear_mask=%x set_mask=%x", - clear_mask, set_mask); - const int old_bits = event_bits_; - const int new_bits = (event_bits_ & ~clear_mask) | set_mask; - event_bits_ = new_bits; - - // If anything changed clear the event and update the event mask. - if (old_bits != new_bits) { - eventfd_t value; - eventfd_read(event_fd_.Get(), &value); - - epoll_event event; - event.events = POLLIN; - event.data.u32 = event_bits_; - if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, event_fd_.Get(), &event) < - 0) { - const int error = errno; - ALOGE("ChannelEventSet::AddEventHandle: Failed to update event: %s", - strerror(error)); - return -error; - } + event.events = EPOLLIN; + event.data.u32 = 0; + if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, pollhup_event_fd.Get(), &event) < + 0) { + const int error = errno; + ALOGE( + "ChannelEventSet::ChannelEventSet: Failed to add pollhup_event_fd: %s", + strerror(error)); + return; } - // If there are any bits set, re-trigger the eventfd. - if (new_bits) - eventfd_write(event_fd_.Get(), 1); - - return 0; + pollin_event_fd_ = std::move(pollin_event_fd); + pollhup_event_fd_ = std::move(pollhup_event_fd); + data_fd_ = std::move(data_fd); + epoll_fd_ = std::move(epoll_fd); } -Status<void> ChannelEventSet::SetupHandle(int fd, LocalHandle* handle, - const char* error_name) { - const int error = errno; - handle->Reset(fd); - if (!*handle) { - ALOGE("ChannelEventSet::SetupHandle: Failed to setup %s handle: %s", - error_name, strerror(error)); +Status<int> ChannelEventReceiver::PollPendingEvents(int timeout_ms) const { + std::array<pollfd, 3> pfds = {{{pollin_event_fd_.Get(), POLLIN, 0}, + {pollhup_event_fd_.Get(), POLLIN, 0}, + {data_fd_.Get(), POLLHUP | POLLRDHUP, 0}}}; + if (RETRY_EINTR(poll(pfds.data(), pfds.size(), timeout_ms)) < 0) { + const int error = errno; + ALOGE( + "ChannelEventReceiver::PollPendingEvents: Failed to poll for events: " + "%s", + strerror(error)); return ErrorStatus{error}; } - return {}; + + const int event_mask = + ((pfds[0].revents & POLLIN) ? EPOLLIN : 0) | + ((pfds[1].revents & POLLIN) ? EPOLLHUP : 0) | + ((pfds[2].revents & (POLLHUP | POLLRDHUP)) ? EPOLLHUP : 0); + return {event_mask}; } Status<int> ChannelEventReceiver::GetPendingEvents() const { constexpr long kTimeoutMs = 0; - epoll_event event; - const int count = - RETRY_EINTR(epoll_wait(epoll_fd_.Get(), &event, 1, kTimeoutMs)); - - Status<int> status; - if (count < 0) { - status.SetError(errno); - ALOGE("ChannelEventReceiver::GetPendingEvents: Failed to get events: %s", - status.GetErrorMessage().c_str()); - return status; - } else if (count == 0) { - status.SetError(ETIMEDOUT); - return status; - } - - const int mask_out = event.data.u32; - ALOGD_IF(TRACE, "ChannelEventReceiver::GetPendingEvents: mask_out=%x", - mask_out); + return PollPendingEvents(kTimeoutMs); +} - status.SetValue(mask_out); - return status; +std::vector<ClientChannel::EventSource> ChannelEventReceiver::GetEventSources() + const { + return {{data_fd_.Get(), EPOLLHUP | EPOLLRDHUP}, + {pollin_event_fd_.Get(), EPOLLIN}, + {pollhup_event_fd_.Get(), POLLIN}}; } } // namespace uds diff --git a/libs/vr/libpdx_uds/channel_manager.cpp b/libs/vr/libpdx_uds/channel_manager.cpp index afc0a4f041..43ebe05021 100644 --- a/libs/vr/libpdx_uds/channel_manager.cpp +++ b/libs/vr/libpdx_uds/channel_manager.cpp @@ -22,18 +22,26 @@ void ChannelManager::CloseHandle(int32_t handle) { } LocalChannelHandle ChannelManager::CreateHandle(LocalHandle data_fd, - LocalHandle event_fd) { - if (data_fd && event_fd) { + LocalHandle pollin_event_fd, + LocalHandle pollhup_event_fd) { + if (data_fd && pollin_event_fd && pollhup_event_fd) { std::lock_guard<std::mutex> autolock(mutex_); - int32_t handle = data_fd.Get(); - channels_.emplace(handle, - ChannelData{std::move(data_fd), std::move(event_fd)}); + const int32_t handle = data_fd.Get(); + channels_.emplace( + handle, + ChannelEventReceiver{std::move(data_fd), std::move(pollin_event_fd), + std::move(pollhup_event_fd)}); return LocalChannelHandle(this, handle); + } else { + ALOGE( + "ChannelManager::CreateHandle: Invalid arguments: data_fd=%d " + "pollin_event_fd=%d pollhup_event_fd=%d", + data_fd.Get(), pollin_event_fd.Get(), pollhup_event_fd.Get()); + return LocalChannelHandle(nullptr, -1); } - return LocalChannelHandle(nullptr, -1); } -ChannelManager::ChannelData* ChannelManager::GetChannelData(int32_t handle) { +ChannelEventReceiver* ChannelManager::GetChannelData(int32_t handle) { std::lock_guard<std::mutex> autolock(mutex_); auto channel = channels_.find(handle); return channel != channels_.end() ? &channel->second : nullptr; diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp index 9d9161784a..2e9c1def3b 100644 --- a/libs/vr/libpdx_uds/client_channel.cpp +++ b/libs/vr/libpdx_uds/client_channel.cpp @@ -33,7 +33,9 @@ struct TransactionState { } else if (static_cast<size_t>(index) < response.channels.size()) { auto& channel_info = response.channels[index]; *handle = ChannelManager::Get().CreateHandle( - std::move(channel_info.data_fd), std::move(channel_info.event_fd)); + std::move(channel_info.data_fd), + std::move(channel_info.pollin_event_fd), + std::move(channel_info.pollhup_event_fd)); } else { return false; } @@ -53,9 +55,9 @@ struct TransactionState { if (auto* channel_data = ChannelManager::Get().GetChannelData(handle.value())) { - ChannelInfo<BorrowedHandle> channel_info; - channel_info.data_fd.Reset(handle.value()); - channel_info.event_fd = channel_data->event_receiver.event_fd(); + ChannelInfo<BorrowedHandle> channel_info{ + channel_data->data_fd(), channel_data->pollin_event_fd(), + channel_data->pollhup_event_fd()}; request.channels.push_back(std::move(channel_info)); return request.channels.size() - 1; } else { @@ -90,10 +92,12 @@ Status<void> SendRequest(const BorrowedHandle& socket_fd, size_t send_len = CountVectorSize(send_vector, send_count); InitRequest(&transaction_state->request, opcode, send_len, max_recv_len, false); - auto status = SendData(socket_fd, transaction_state->request); - if (status && send_len > 0) - status = SendDataVector(socket_fd, send_vector, send_count); - return status; + if (send_len == 0) { + send_vector = nullptr; + send_count = 0; + } + return SendData(socket_fd, transaction_state->request, send_vector, + send_count); } Status<void> ReceiveResponse(const BorrowedHandle& socket_fd, diff --git a/libs/vr/libpdx_uds/client_channel_factory.cpp b/libs/vr/libpdx_uds/client_channel_factory.cpp index 433f459769..09dc7beb76 100644 --- a/libs/vr/libpdx_uds/client_channel_factory.cpp +++ b/libs/vr/libpdx_uds/client_channel_factory.cpp @@ -139,20 +139,33 @@ Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect( RequestHeader<BorrowedHandle> request; InitRequest(&request, opcodes::CHANNEL_OPEN, 0, 0, false); + status = SendData(socket_.Borrow(), request); if (!status) return status.error_status(); + ResponseHeader<LocalHandle> response; status = ReceiveData(socket_.Borrow(), &response); if (!status) return status.error_status(); - int ref = response.ret_code; - if (ref < 0 || static_cast<size_t>(ref) > response.file_descriptors.size()) + else if (response.ret_code < 0 || response.channels.size() != 1) + return ErrorStatus(EIO); + + LocalHandle pollin_event_fd = std::move(response.channels[0].pollin_event_fd); + LocalHandle pollhup_event_fd = + std::move(response.channels[0].pollhup_event_fd); + + if (!pollin_event_fd || !pollhup_event_fd) { + ALOGE( + "ClientChannelFactory::Connect: Required fd was not returned from the " + "service: pollin_event_fd=%d pollhup_event_fd=%d", + pollin_event_fd.Get(), pollhup_event_fd.Get()); return ErrorStatus(EIO); + } - LocalHandle event_fd = std::move(response.file_descriptors[ref]); return ClientChannel::Create(ChannelManager::Get().CreateHandle( - std::move(socket_), std::move(event_fd))); + std::move(socket_), std::move(pollin_event_fd), + std::move(pollhup_event_fd))); } } // namespace uds diff --git a/libs/vr/libpdx_uds/client_channel_tests.cpp b/libs/vr/libpdx_uds/client_channel_tests.cpp index 7c3c68aa31..15600305a1 100644 --- a/libs/vr/libpdx_uds/client_channel_tests.cpp +++ b/libs/vr/libpdx_uds/client_channel_tests.cpp @@ -13,6 +13,7 @@ #include <pdx/client.h> #include <pdx/rpc/remote_method.h> #include <pdx/service.h> +#include <pdx/service_dispatcher.h> #include <uds/client_channel_factory.h> #include <uds/service_endpoint.h> @@ -81,7 +82,7 @@ class TestServiceRunner { auto endpoint = Endpoint::CreateFromSocketFd(LocalHandle{}); endpoint->RegisterNewChannelForTests(std::move(channel_socket)); service_ = TestService::Create(std::move(endpoint)); - dispatcher_ = android::pdx::uds::ServiceDispatcher::Create(); + dispatcher_ = ServiceDispatcher::Create(); dispatcher_->AddService(service_); dispatch_thread_ = std::thread( std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get())); diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp index d75ce86e4b..f85b3bb666 100644 --- a/libs/vr/libpdx_uds/ipc_helper.cpp +++ b/libs/vr/libpdx_uds/ipc_helper.cpp @@ -20,6 +20,9 @@ namespace uds { namespace { +constexpr size_t kMaxFdCount = + 256; // Total of 1KiB of data to transfer these FDs. + // Default implementations of Send/Receive interfaces to use standard socket // send/sendmsg/recv/recvmsg functions. class SocketSender : public SendInterface { @@ -175,20 +178,31 @@ Status<void> SendPayload::Send(const BorrowedHandle& socket_fd) { } Status<void> SendPayload::Send(const BorrowedHandle& socket_fd, - const ucred* cred) { + const ucred* cred, const iovec* data_vec, + size_t vec_count) { + if (file_handles_.size() > kMaxFdCount) { + ALOGE( + "SendPayload::Send: Trying to send too many file descriptors (%zu), " + "max allowed = %zu", + file_handles_.size(), kMaxFdCount); + return ErrorStatus{EINVAL}; + } + SendInterface* sender = sender_ ? sender_ : &g_socket_sender; MessagePreamble preamble; preamble.magic = kMagicPreamble; preamble.data_size = buffer_.size(); preamble.fd_count = file_handles_.size(); - Status<void> ret = SendAll(sender, socket_fd, &preamble, sizeof(preamble)); - if (!ret) - return ret; msghdr msg = {}; - iovec recv_vect = {buffer_.data(), buffer_.size()}; - msg.msg_iov = &recv_vect; - msg.msg_iovlen = 1; + msg.msg_iovlen = 2 + vec_count; + msg.msg_iov = static_cast<iovec*>(alloca(sizeof(iovec) * msg.msg_iovlen)); + msg.msg_iov[0].iov_base = &preamble; + msg.msg_iov[0].iov_len = sizeof(preamble); + msg.msg_iov[1].iov_base = buffer_.data(); + msg.msg_iov[1].iov_len = buffer_.size(); + for (size_t i = 0; i < vec_count; i++) + msg.msg_iov[i + 2] = data_vec[i]; if (cred || !file_handles_.empty()) { const size_t fd_bytes = file_handles_.size() * sizeof(int); @@ -270,7 +284,15 @@ Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd, ucred* cred) { RecvInterface* receiver = receiver_ ? receiver_ : &g_socket_receiver; MessagePreamble preamble; - Status<void> ret = RecvAll(receiver, socket_fd, &preamble, sizeof(preamble)); + msghdr msg = {}; + iovec recv_vect = {&preamble, sizeof(preamble)}; + msg.msg_iov = &recv_vect; + msg.msg_iovlen = 1; + const size_t receive_fd_bytes = kMaxFdCount * sizeof(int); + msg.msg_controllen = CMSG_SPACE(sizeof(ucred)) + CMSG_SPACE(receive_fd_bytes); + msg.msg_control = alloca(msg.msg_controllen); + + Status<void> ret = RecvMsgAll(receiver, socket_fd, &msg); if (!ret) return ret; @@ -284,23 +306,6 @@ Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd, file_handles_.clear(); read_pos_ = 0; - msghdr msg = {}; - iovec recv_vect = {buffer_.data(), buffer_.size()}; - msg.msg_iov = &recv_vect; - msg.msg_iovlen = 1; - - if (cred || preamble.fd_count) { - const size_t receive_fd_bytes = preamble.fd_count * sizeof(int); - msg.msg_controllen = - (cred ? CMSG_SPACE(sizeof(ucred)) : 0) + - (receive_fd_bytes == 0 ? 0 : CMSG_SPACE(receive_fd_bytes)); - msg.msg_control = alloca(msg.msg_controllen); - } - - ret = RecvMsgAll(receiver, socket_fd, &msg); - if (!ret) - return ret; - bool cred_available = false; file_handles_.reserve(preamble.fd_count); cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); @@ -320,6 +325,10 @@ Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd, cmsg = CMSG_NXTHDR(&msg, cmsg); } + ret = RecvAll(receiver, socket_fd, buffer_.data(), buffer_.size()); + if (!ret) + return ret; + if (cred && !cred_available) { ALOGE("ReceivePayload::Receive: Failed to obtain message credentials"); ret.SetError(EIO); diff --git a/libs/vr/libpdx_uds/private/uds/channel_event_set.h b/libs/vr/libpdx_uds/private/uds/channel_event_set.h index 1f464d5f91..99e75028d1 100644 --- a/libs/vr/libpdx_uds/private/uds/channel_event_set.h +++ b/libs/vr/libpdx_uds/private/uds/channel_event_set.h @@ -1,11 +1,9 @@ #ifndef ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_ #define ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_ -#include <errno.h> -#include <poll.h> -#include <sys/epoll.h> -#include <sys/eventfd.h> +#include <vector> +#include <pdx/client_channel.h> #include <pdx/file_handle.h> #include <pdx/status.h> @@ -19,21 +17,20 @@ class ChannelEventSet { ChannelEventSet(ChannelEventSet&&) = default; ChannelEventSet& operator=(ChannelEventSet&&) = default; - BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); } + BorrowedHandle pollin_event_fd() const { return pollin_event_fd_.Borrow(); } + BorrowedHandle pollhup_event_fd() const { return pollhup_event_fd_.Borrow(); } - explicit operator bool() const { return !!epoll_fd_ && !!event_fd_; } + explicit operator bool() const { + return !!pollin_event_fd_ && !!pollhup_event_fd_; + } - Status<void> AddDataFd(const LocalHandle& data_fd); int ModifyEvents(int clear_mask, int set_mask); private: - LocalHandle epoll_fd_; - LocalHandle event_fd_; + LocalHandle pollin_event_fd_; + LocalHandle pollhup_event_fd_; uint32_t event_bits_ = 0; - static Status<void> SetupHandle(int fd, LocalHandle* handle, - const char* error_name); - ChannelEventSet(const ChannelEventSet&) = delete; void operator=(const ChannelEventSet&) = delete; }; @@ -41,14 +38,31 @@ class ChannelEventSet { class ChannelEventReceiver { public: ChannelEventReceiver() = default; - ChannelEventReceiver(LocalHandle epoll_fd) : epoll_fd_{std::move(epoll_fd)} {} + ChannelEventReceiver(LocalHandle data_fd, LocalHandle pollin_event_fd, + LocalHandle pollhup_event_fd); ChannelEventReceiver(ChannelEventReceiver&&) = default; ChannelEventReceiver& operator=(ChannelEventReceiver&&) = default; + explicit operator bool() const { + return !!pollin_event_fd_ && !!pollhup_event_fd_ && !!data_fd_ && + !!epoll_fd_; + } + BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); } + + BorrowedHandle pollin_event_fd() const { return pollin_event_fd_.Borrow(); } + BorrowedHandle pollhup_event_fd() const { return pollhup_event_fd_.Borrow(); } + BorrowedHandle data_fd() const { return data_fd_.Borrow(); } + Status<int> GetPendingEvents() const; + Status<int> PollPendingEvents(int timeout_ms) const; + + std::vector<ClientChannel::EventSource> GetEventSources() const; private: + LocalHandle data_fd_; + LocalHandle pollin_event_fd_; + LocalHandle pollhup_event_fd_; LocalHandle epoll_fd_; ChannelEventReceiver(const ChannelEventReceiver&) = delete; diff --git a/libs/vr/libpdx_uds/private/uds/channel_manager.h b/libs/vr/libpdx_uds/private/uds/channel_manager.h index 2aca41421a..5f6a514340 100644 --- a/libs/vr/libpdx_uds/private/uds/channel_manager.h +++ b/libs/vr/libpdx_uds/private/uds/channel_manager.h @@ -16,13 +16,11 @@ class ChannelManager : public ChannelManagerInterface { public: static ChannelManager& Get(); - LocalChannelHandle CreateHandle(LocalHandle data_fd, LocalHandle event_fd); - struct ChannelData { - LocalHandle data_fd; - ChannelEventReceiver event_receiver; - }; + LocalChannelHandle CreateHandle(LocalHandle data_fd, + LocalHandle pollin_event_fd, + LocalHandle pollhup_event_fd); - ChannelData* GetChannelData(int32_t handle); + ChannelEventReceiver* GetChannelData(int32_t handle); private: ChannelManager() = default; @@ -30,7 +28,7 @@ class ChannelManager : public ChannelManagerInterface { void CloseHandle(int32_t handle) override; std::mutex mutex_; - std::unordered_map<int32_t, ChannelData> channels_; + std::unordered_map<int32_t, ChannelEventReceiver> channels_; }; } // namespace uds diff --git a/libs/vr/libpdx_uds/private/uds/client_channel.h b/libs/vr/libpdx_uds/private/uds/client_channel.h index 8f607f56a1..7a5ddf40eb 100644 --- a/libs/vr/libpdx_uds/private/uds/client_channel.h +++ b/libs/vr/libpdx_uds/private/uds/client_channel.h @@ -23,11 +23,19 @@ class ClientChannel : public pdx::ClientChannel { uint32_t GetIpcTag() const override { return Endpoint::kIpcTag; } int event_fd() const override { - return channel_data_ ? channel_data_->event_receiver.event_fd().Get() : -1; + return channel_data_ ? channel_data_->event_fd().Get() : -1; } + + std::vector<EventSource> GetEventSources() const override { + if (channel_data_) + return channel_data_->GetEventSources(); + else + return {}; + } + Status<int> GetEventMask(int /*events*/) override { if (channel_data_) - return channel_data_->event_receiver.GetPendingEvents(); + return channel_data_->GetPendingEvents(); else return ErrorStatus(EINVAL); } @@ -74,7 +82,7 @@ class ClientChannel : public pdx::ClientChannel { const iovec* receive_vector, size_t receive_count); LocalChannelHandle channel_handle_; - ChannelManager::ChannelData* channel_data_; + ChannelEventReceiver* channel_data_; std::mutex socket_mutex_; }; diff --git a/libs/vr/libpdx_uds/private/uds/ipc_helper.h b/libs/vr/libpdx_uds/private/uds/ipc_helper.h index bde16d3d31..63b5b1078a 100644 --- a/libs/vr/libpdx_uds/private/uds/ipc_helper.h +++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h @@ -59,7 +59,8 @@ class SendPayload : public MessageWriter, public OutputResourceMapper { public: SendPayload(SendInterface* sender = nullptr) : sender_{sender} {} Status<void> Send(const BorrowedHandle& socket_fd); - Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred); + Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred, + const iovec* data_vec = nullptr, size_t vec_count = 0); // MessageWriter void* GetNextWriteBufferSection(size_t size) override; @@ -109,10 +110,12 @@ template <typename FileHandleType> class ChannelInfo { public: FileHandleType data_fd; - FileHandleType event_fd; + FileHandleType pollin_event_fd; + FileHandleType pollhup_event_fd; private: - PDX_SERIALIZABLE_MEMBERS(ChannelInfo, data_fd, event_fd); + PDX_SERIALIZABLE_MEMBERS(ChannelInfo, data_fd, pollin_event_fd, + pollhup_event_fd); }; template <typename FileHandleType> @@ -156,18 +159,22 @@ class ResponseHeader { }; template <typename T> -inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data) { +inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data, + const iovec* data_vec = nullptr, + size_t vec_count = 0) { SendPayload payload; rpc::Serialize(data, &payload); - return payload.Send(socket_fd); + return payload.Send(socket_fd, nullptr, data_vec, vec_count); } template <typename FileHandleType> inline Status<void> SendData(const BorrowedHandle& socket_fd, - const RequestHeader<FileHandleType>& request) { + const RequestHeader<FileHandleType>& request, + const iovec* data_vec = nullptr, + size_t vec_count = 0) { SendPayload payload; rpc::Serialize(request, &payload); - return payload.Send(socket_fd, &request.cred); + return payload.Send(socket_fd, &request.cred, data_vec, vec_count); } Status<void> SendData(const BorrowedHandle& socket_fd, const void* data, diff --git a/libs/vr/libpdx_uds/private/uds/service_dispatcher.h b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h deleted file mode 100644 index 23af4f4403..0000000000 --- a/libs/vr/libpdx_uds/private/uds/service_dispatcher.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_ -#define ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_ - -#include <list> -#include <memory> -#include <mutex> -#include <unordered_map> - -#include <pdx/file_handle.h> -#include <pdx/service_dispatcher.h> - -namespace android { -namespace pdx { -namespace uds { - -class ServiceDispatcher : public pdx::ServiceDispatcher { - public: - // Get a new instance of ServiceDispatcher, or return nullptr if init failed. - static std::unique_ptr<pdx::ServiceDispatcher> Create(); - - ~ServiceDispatcher() override; - int AddService(const std::shared_ptr<Service>& service) override; - int RemoveService(const std::shared_ptr<Service>& service) override; - int ReceiveAndDispatch() override; - int ReceiveAndDispatch(int timeout) override; - int EnterDispatchLoop() override; - void SetCanceled(bool cancel) override; - bool IsCanceled() const override; - - private: - ServiceDispatcher(); - - // Internal thread accounting. - int ThreadEnter(); - void ThreadExit(); - - std::mutex mutex_; - std::condition_variable condition_; - std::atomic<bool> canceled_{false}; - - std::list<std::shared_ptr<Service>> services_; - - int thread_count_ = 0; - LocalHandle event_fd_; - LocalHandle epoll_fd_; - - ServiceDispatcher(const ServiceDispatcher&) = delete; - void operator=(const ServiceDispatcher&) = delete; -}; - -} // namespace uds -} // namespace pdx -} // namespace android - -#endif // ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_ diff --git a/libs/vr/libpdx_uds/private/uds/service_endpoint.h b/libs/vr/libpdx_uds/private/uds/service_endpoint.h index 368891ce05..01ebf6519a 100644 --- a/libs/vr/libpdx_uds/private/uds/service_endpoint.h +++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h @@ -7,12 +7,12 @@ #include <mutex> #include <string> #include <unordered_map> +#include <utility> #include <vector> #include <pdx/service.h> #include <pdx/service_endpoint.h> #include <uds/channel_event_set.h> -#include <uds/service_dispatcher.h> namespace android { namespace pdx { @@ -105,7 +105,7 @@ class Endpoint : public pdx::Endpoint { // socket file descriptor. Status<void> RegisterNewChannelForTests(LocalHandle channel_fd); - int epoll_fd() const { return epoll_fd_.Get(); } + int epoll_fd() const override { return epoll_fd_.Get(); } private: struct ChannelData { @@ -140,7 +140,8 @@ class Endpoint : public pdx::Endpoint { Status<void> ReenableEpollEvent(const BorrowedHandle& channel_fd); Channel* GetChannelState(int32_t channel_id); BorrowedHandle GetChannelSocketFd(int32_t channel_id); - BorrowedHandle GetChannelEventFd(int32_t channel_id); + Status<std::pair<BorrowedHandle, BorrowedHandle>> GetChannelEventFd( + int32_t channel_id); int32_t GetChannelId(const BorrowedHandle& channel_fd); Status<void> CreateChannelSocketPair(LocalHandle* local_socket, LocalHandle* remote_socket); diff --git a/libs/vr/libpdx_uds/remote_method_tests.cpp b/libs/vr/libpdx_uds/remote_method_tests.cpp index 3109753dc2..3f25776e14 100644 --- a/libs/vr/libpdx_uds/remote_method_tests.cpp +++ b/libs/vr/libpdx_uds/remote_method_tests.cpp @@ -15,9 +15,9 @@ #include <pdx/rpc/remote_method.h> #include <pdx/rpc/serializable.h> #include <pdx/service.h> +#include <pdx/service_dispatcher.h> #include <uds/client_channel.h> #include <uds/client_channel_factory.h> -#include <uds/service_dispatcher.h> #include <uds/service_endpoint.h> using android::pdx::BorrowedHandle; @@ -561,7 +561,7 @@ class RemoteMethodTest : public ::testing::Test { void SetUp() override { // Create a dispatcher to handle messages to services. - dispatcher_ = android::pdx::uds::ServiceDispatcher::Create(); + dispatcher_ = android::pdx::ServiceDispatcher::Create(); ASSERT_NE(nullptr, dispatcher_); // Start the message dispatch loop in a separate thread. diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp index 27a56f9fe0..0ee77f43a6 100644 --- a/libs/vr/libpdx_uds/service_endpoint.cpp +++ b/libs/vr/libpdx_uds/service_endpoint.cpp @@ -49,7 +49,9 @@ struct MessageState { } else if (static_cast<size_t>(index) < request.channels.size()) { auto& channel_info = request.channels[index]; *handle = ChannelManager::Get().CreateHandle( - std::move(channel_info.data_fd), std::move(channel_info.event_fd)); + std::move(channel_info.data_fd), + std::move(channel_info.pollin_event_fd), + std::move(channel_info.pollhup_event_fd)); } else { return false; } @@ -69,9 +71,9 @@ struct MessageState { if (auto* channel_data = ChannelManager::Get().GetChannelData(handle.value())) { - ChannelInfo<BorrowedHandle> channel_info; - channel_info.data_fd.Reset(handle.value()); - channel_info.event_fd = channel_data->event_receiver.event_fd(); + ChannelInfo<BorrowedHandle> channel_info{ + channel_data->data_fd(), channel_data->pollin_event_fd(), + channel_data->pollhup_event_fd()}; response.channels.push_back(std::move(channel_info)); return response.channels.size() - 1; } else { @@ -80,12 +82,13 @@ struct MessageState { } Status<ChannelReference> PushChannelHandle(BorrowedHandle data_fd, - BorrowedHandle event_fd) { - if (!data_fd || !event_fd) + BorrowedHandle pollin_event_fd, + BorrowedHandle pollhup_event_fd) { + if (!data_fd || !pollin_event_fd || !pollhup_event_fd) return ErrorStatus{EINVAL}; - ChannelInfo<BorrowedHandle> channel_info; - channel_info.data_fd = std::move(data_fd); - channel_info.event_fd = std::move(event_fd); + ChannelInfo<BorrowedHandle> channel_info{std::move(data_fd), + std::move(pollin_event_fd), + std::move(pollhup_event_fd)}; response.channels.push_back(std::move(channel_info)); return response.channels.size() - 1; } @@ -287,7 +290,6 @@ Status<std::pair<int32_t, Endpoint::ChannelData*>> Endpoint::OnNewChannelLocked( return ErrorStatus(errno); } ChannelData channel_data; - channel_data.event_set.AddDataFd(channel_fd); channel_data.data_fd = std::move(channel_fd); channel_data.channel_state = channel_state; for (;;) { @@ -431,18 +433,21 @@ Status<RemoteChannelHandle> Endpoint::PushChannel(Message* message, return status.error_status(); std::lock_guard<std::mutex> autolock(channel_mutex_); - auto channel_data = OnNewChannelLocked(std::move(local_socket), channel); - if (!channel_data) - return channel_data.error_status(); - *channel_id = channel_data.get().first; + auto channel_data_status = + OnNewChannelLocked(std::move(local_socket), channel); + if (!channel_data_status) + return channel_data_status.error_status(); + + ChannelData* channel_data; + std::tie(*channel_id, channel_data) = channel_data_status.take(); // Flags are ignored for now. // TODO(xiaohuit): Implement those. auto* state = static_cast<MessageState*>(message->GetState()); Status<ChannelReference> ref = state->PushChannelHandle( - remote_socket.Borrow(), - channel_data.get().second->event_set.event_fd().Borrow()); + remote_socket.Borrow(), channel_data->event_set.pollin_event_fd(), + channel_data->event_set.pollhup_event_fd()); if (!ref) return ref.error_status(); state->sockets_to_close.push_back(std::move(remote_socket)); @@ -472,13 +477,15 @@ BorrowedHandle Endpoint::GetChannelSocketFd(int32_t channel_id) { return handle; } -BorrowedHandle Endpoint::GetChannelEventFd(int32_t channel_id) { +Status<std::pair<BorrowedHandle, BorrowedHandle>> Endpoint::GetChannelEventFd( + int32_t channel_id) { std::lock_guard<std::mutex> autolock(channel_mutex_); - BorrowedHandle handle; auto channel_data = channels_.find(channel_id); - if (channel_data != channels_.end()) - handle = channel_data->second.event_set.event_fd().Borrow(); - return handle; + if (channel_data != channels_.end()) { + return {{channel_data->second.event_set.pollin_event_fd(), + channel_data->second.event_set.pollhup_event_fd()}}; + } + return ErrorStatus(ENOENT); } int32_t Endpoint::GetChannelId(const BorrowedHandle& channel_fd) { @@ -593,11 +600,6 @@ Status<void> Endpoint::MessageReceive(Message* message) { } BorrowedHandle channel_fd{event.data.fd}; - if (event.events & (EPOLLRDHUP | EPOLLHUP)) { - BuildCloseMessage(GetChannelId(channel_fd), message); - return {}; - } - return ReceiveMessageForChannel(channel_fd, message); } @@ -616,12 +618,23 @@ Status<void> Endpoint::MessageReply(Message* message, int return_code) { if (return_code < 0) { return CloseChannel(channel_id); } else { - // Reply with the event fd. - auto push_status = state->PushFileHandle(GetChannelEventFd(channel_id)); - state->response_data.clear(); // Just in case... - if (!push_status) - return push_status.error_status(); - return_code = push_status.get(); + // Open messages do not have a payload and may not transfer any channels + // or file descriptors on behalf of the service. + state->response_data.clear(); + state->response.file_descriptors.clear(); + state->response.channels.clear(); + + // Return the channel event-related fds in a single ChannelInfo entry + // with an empty data_fd member. + auto status = GetChannelEventFd(channel_id); + if (!status) + return status.error_status(); + + auto handles = status.take(); + state->response.channels.push_back({BorrowedHandle(), + std::move(handles.first), + std::move(handles.second)}); + return_code = 0; } break; } diff --git a/libs/vr/libpdx_uds/service_framework_tests.cpp b/libs/vr/libpdx_uds/service_framework_tests.cpp index 2943239495..27427162f5 100644 --- a/libs/vr/libpdx_uds/service_framework_tests.cpp +++ b/libs/vr/libpdx_uds/service_framework_tests.cpp @@ -1,5 +1,6 @@ #include <errno.h> #include <fcntl.h> +#include <poll.h> #include <sys/epoll.h> #include <sys/eventfd.h> #include <unistd.h> @@ -16,10 +17,10 @@ #include <pdx/client.h> #include <pdx/file_handle.h> #include <pdx/service.h> +#include <pdx/service_dispatcher.h> #include <private/android_filesystem_config.h> #include <uds/client_channel.h> #include <uds/client_channel_factory.h> -#include <uds/service_dispatcher.h> #include <uds/service_endpoint.h> using android::pdx::BorrowedChannelHandle; @@ -425,7 +426,7 @@ class ServiceFrameworkTest : public ::testing::Test { void SetUp() override { // Create a dispatcher to handle messages to services. - dispatcher_ = android::pdx::uds::ServiceDispatcher::Create(); + dispatcher_ = android::pdx::ServiceDispatcher::Create(); ASSERT_NE(nullptr, dispatcher_); // Start the message dispatch loop in a separate thread. @@ -506,6 +507,37 @@ TEST_F(ServiceFrameworkTest, Impulse) { EXPECT_EQ(-EINVAL, client->SendAsync(invalid_pointer, sizeof(int))); } +// Test impulses. +TEST_F(ServiceFrameworkTest, ImpulseHangup) { + // Create a test service and add it to the dispatcher. + auto service = TestService::Create(kTestService1); + ASSERT_NE(nullptr, service); + ASSERT_EQ(0, dispatcher_->AddService(service)); + + auto client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, client); + + const int kMaxIterations = 1000; + for (int i = 0; i < kMaxIterations; i++) { + auto impulse_client = TestClient::Create(kTestService1); + ASSERT_NE(nullptr, impulse_client); + + const uint8_t a = (i >> 0) & 0xff; + const uint8_t b = (i >> 8) & 0xff; + const uint8_t c = (i >> 16) & 0xff; + const uint8_t d = (i >> 24) & 0xff; + ImpulsePayload expected_payload = {{a, b, c, d}}; + EXPECT_EQ(0, impulse_client->SendAsync(expected_payload.data(), 4)); + + // Hangup the impulse test client, then send a sync message over client to + // make sure the hangup message is handled before checking the impulse + // payload. + impulse_client = nullptr; + client->GetThisChannelId(); + EXPECT_EQ(expected_payload, service->GetImpulsePayload()); + } +} + // Test Message::PushChannel/Service::PushChannel API. TEST_F(ServiceFrameworkTest, PushChannel) { // Create a test service and add it to the dispatcher. @@ -574,9 +606,7 @@ TEST_F(ServiceFrameworkTest, Ids) { pid_t process_id2; - std::thread thread([&]() { - process_id2 = client->GetThisProcessId(); - }); + std::thread thread([&]() { process_id2 = client->GetThisProcessId(); }); thread.join(); EXPECT_LT(2, process_id2); @@ -614,15 +644,15 @@ TEST_F(ServiceFrameworkTest, PollIn) { auto client = TestClient::Create(kTestService1); ASSERT_NE(nullptr, client); - epoll_event event; - int count = epoll_wait(client->event_fd(), &event, 1, 0); + pollfd pfd{client->event_fd(), POLLIN, 0}; + int count = poll(&pfd, 1, 0); ASSERT_EQ(0, count); client->SendPollInEvent(); - count = epoll_wait(client->event_fd(), &event, 1, -1); + count = poll(&pfd, 1, 10000 /*10s*/); ASSERT_EQ(1, count); - ASSERT_TRUE((EPOLLIN & event.events) != 0); + ASSERT_TRUE((POLLIN & pfd.revents) != 0); } TEST_F(ServiceFrameworkTest, PollHup) { @@ -635,15 +665,15 @@ TEST_F(ServiceFrameworkTest, PollHup) { auto client = TestClient::Create(kTestService1); ASSERT_NE(nullptr, client); - epoll_event event; - int count = epoll_wait(client->event_fd(), &event, 1, 0); + pollfd pfd{client->event_fd(), POLLIN, 0}; + int count = poll(&pfd, 1, 0); ASSERT_EQ(0, count); client->SendPollHupEvent(); - count = epoll_wait(client->event_fd(), &event, 1, -1); + count = poll(&pfd, 1, 10000 /*10s*/); ASSERT_EQ(1, count); - auto event_status = client->GetEventMask(event.events); + auto event_status = client->GetEventMask(pfd.revents); ASSERT_TRUE(event_status.ok()); ASSERT_TRUE((EPOLLHUP & event_status.get()) != 0); } diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp index fda9585dc4..9614c6d306 100644 --- a/libs/vr/libvrflinger/acquired_buffer.cpp +++ b/libs/vr/libvrflinger/acquired_buffer.cpp @@ -9,8 +9,8 @@ namespace android { namespace dvr { AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, - LocalHandle acquire_fence) - : buffer_(buffer), acquire_fence_(std::move(acquire_fence)) {} + LocalHandle acquire_fence, std::size_t slot) + : buffer_(buffer), acquire_fence_(std::move(acquire_fence)), slot_(slot) {} AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, int* error) { @@ -31,18 +31,20 @@ AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, } } -AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other) - : buffer_(std::move(other.buffer_)), - acquire_fence_(std::move(other.acquire_fence_)) {} +AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other) { + *this = std::move(other); +} AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); } AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) { if (this != &other) { - Release(LocalHandle(kEmptyFence)); + Release(); - buffer_ = std::move(other.buffer_); - acquire_fence_ = std::move(other.acquire_fence_); + using std::swap; + swap(buffer_, other.buffer_); + swap(acquire_fence_, other.acquire_fence_); + swap(slot_, other.slot_); } return *this; } @@ -81,8 +83,6 @@ int AcquiredBuffer::Release(LocalHandle release_fence) { ALOGD_IF(TRACE, "AcquiredBuffer::Release: buffer_id=%d release_fence=%d", buffer_ ? buffer_->id() : -1, release_fence.Get()); if (buffer_) { - // Close the release fence since we can't transfer it with an async release. - release_fence.Close(); const int ret = buffer_->ReleaseAsync(); if (ret < 0) { ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s", @@ -92,9 +92,10 @@ int AcquiredBuffer::Release(LocalHandle release_fence) { } buffer_ = nullptr; - acquire_fence_.Close(); } + acquire_fence_.Close(); + slot_ = 0; return 0; } diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h index e0dc9f2ac2..32e912a65d 100644 --- a/libs/vr/libvrflinger/acquired_buffer.h +++ b/libs/vr/libvrflinger/acquired_buffer.h @@ -21,7 +21,7 @@ class AcquiredBuffer { // this constructor; the constructor does not attempt to ACQUIRE the buffer // itself. AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, - pdx::LocalHandle acquire_fence); + pdx::LocalHandle acquire_fence, std::size_t slot = 0); // Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST // be in the POSTED state prior to calling this constructor, as this @@ -64,13 +64,18 @@ class AcquiredBuffer { // to the producer. On success, the BufferConsumer and acquire fence are set // to empty state; if release fails, the BufferConsumer and acquire fence are // left in place and a negative error code is returned. - int Release(pdx::LocalHandle release_fence); + int Release(pdx::LocalHandle release_fence = {}); + + // Returns the slot in the queue this buffer belongs to. Buffers that are not + // part of a queue return 0. + std::size_t slot() const { return slot_; } private: std::shared_ptr<BufferConsumer> buffer_; // Mutable so that the fence can be closed when it is determined to be // signaled during IsAvailable(). mutable pdx::LocalHandle acquire_fence_; + std::size_t slot_{0}; AcquiredBuffer(const AcquiredBuffer&) = delete; void operator=(const AcquiredBuffer&) = delete; diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp index 40396b90c5..ef8cca38dd 100644 --- a/libs/vr/libvrflinger/display_manager_service.cpp +++ b/libs/vr/libvrflinger/display_manager_service.cpp @@ -65,6 +65,7 @@ void DisplayManagerService::OnChannelClose( } pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) { + ATRACE_NAME("DisplayManagerService::HandleMessage"); auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel()); switch (message.GetOp()) { diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp index 733edc659c..ac68a5e3a4 100644 --- a/libs/vr/libvrflinger/display_service.cpp +++ b/libs/vr/libvrflinger/display_service.cpp @@ -40,10 +40,8 @@ namespace dvr { DisplayService::DisplayService(Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback) : BASE("DisplayService", - Endpoint::Create(display::DisplayProtocol::kClientPath)), - hardware_composer_(hidl, request_display_callback), - request_display_callback_(request_display_callback) { - hardware_composer_.Initialize(); + Endpoint::Create(display::DisplayProtocol::kClientPath)) { + hardware_composer_.Initialize(hidl, request_display_callback); } bool DisplayService::IsInitialized() const { @@ -126,6 +124,8 @@ void DisplayService::OnChannelClose(pdx::Message& message, // surface-specific messages to the per-instance handlers. Status<void> DisplayService::HandleMessage(pdx::Message& message) { ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp()); + ATRACE_NAME("DisplayService::HandleMessage"); + switch (message.GetOp()) { case DisplayProtocol::GetMetrics::Opcode: DispatchRemoteMethod<DisplayProtocol::GetMetrics>( @@ -245,18 +245,16 @@ Status<display::SurfaceInfo> DisplayService::OnCreateSurface( surface_status.GetErrorMessage().c_str()); return ErrorStatus(surface_status.error()); } + auto surface = surface_status.take(); + message.SetChannel(surface); - SurfaceType surface_type = surface_status.get()->surface_type(); - display::SurfaceUpdateFlags update_flags = - surface_status.get()->update_flags(); - display::SurfaceInfo surface_info{surface_status.get()->surface_id(), - surface_status.get()->visible(), - surface_status.get()->z_order()}; - - message.SetChannel(surface_status.take()); + // Update the surface with the attributes supplied with the create call. For + // application surfaces this has the side effect of notifying the display + // manager of the new surface. For direct surfaces, this may trigger a mode + // change, depending on the value of the visible attribute. + surface->OnSetAttributes(message, attributes); - SurfaceUpdated(surface_type, update_flags); - return {surface_info}; + return {{surface->surface_id(), surface->visible(), surface->z_order()}}; } void DisplayService::SurfaceUpdated(SurfaceType surface_type, @@ -353,17 +351,9 @@ DisplayService::GetVisibleDisplaySurfaces() const { void DisplayService::UpdateActiveDisplaySurfaces() { auto visible_surfaces = GetVisibleDisplaySurfaces(); - - std::sort(visible_surfaces.begin(), visible_surfaces.end(), - [](const std::shared_ptr<DisplaySurface>& a, - const std::shared_ptr<DisplaySurface>& b) { - return a->z_order() < b->z_order(); - }); - ALOGD_IF(TRACE, "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces", visible_surfaces.size()); - hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces)); } @@ -398,10 +388,6 @@ pdx::Status<void> DisplayService::DeleteGlobalBuffer(DvrGlobalBufferKey key) { return {0}; } -void DisplayService::OnHardwareComposerRefresh() { - hardware_composer_.OnHardwareComposerRefresh(); -} - void DisplayService::SetDisplayConfigurationUpdateNotifier( DisplayConfigurationUpdateNotifier update_notifier) { update_notifier_ = update_notifier; diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h index 6efe264b09..55e33ab852 100644 --- a/libs/vr/libvrflinger/display_service.h +++ b/libs/vr/libvrflinger/display_service.h @@ -72,8 +72,6 @@ class DisplayService : public pdx::ServiceBase<DisplayService> { void GrantDisplayOwnership() { hardware_composer_.Enable(); } void SeizeDisplayOwnership() { hardware_composer_.Disable(); } - void OnHardwareComposerRefresh(); - private: friend BASE; friend DisplaySurface; @@ -119,7 +117,6 @@ class DisplayService : public pdx::ServiceBase<DisplayService> { pdx::Status<void> HandleSurfaceMessage(pdx::Message& message); HardwareComposer hardware_composer_; - RequestDisplayCallback request_display_callback_; EpollEventDispatcher dispatcher_; DisplayConfigurationUpdateNotifier update_notifier_; diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp index 0d6a732a8e..87c823e5b9 100644 --- a/libs/vr/libvrflinger/display_surface.cpp +++ b/libs/vr/libvrflinger/display_surface.cpp @@ -26,14 +26,12 @@ namespace dvr { DisplaySurface::DisplaySurface(DisplayService* service, SurfaceType surface_type, int surface_id, - int process_id, int user_id, - const display::SurfaceAttributes& attributes) + int process_id, int user_id) : service_(service), surface_type_(surface_type), surface_id_(surface_id), process_id_(process_id), user_id_(user_id), - attributes_(attributes), update_flags_(display::SurfaceUpdateFlags::NewSurface) {} DisplaySurface::~DisplaySurface() { @@ -215,8 +213,8 @@ Status<LocalChannelHandle> ApplicationDisplaySurface::OnCreateQueue( ATRACE_NAME("ApplicationDisplaySurface::OnCreateQueue"); ALOGD_IF(TRACE, "ApplicationDisplaySurface::OnCreateQueue: surface_id=%d, " - "meta_size_bytes=%zu", - surface_id(), config.meta_size_bytes); + "user_metadata_size=%zu", + surface_id(), config.user_metadata_size); std::lock_guard<std::mutex> autolock(lock_); auto producer = ProducerQueue::Create(config, UsagePolicy{}); @@ -282,10 +280,10 @@ std::vector<int32_t> DirectDisplaySurface::GetQueueIds() const { Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue( Message& /*message*/, const ProducerQueueConfig& config) { ATRACE_NAME("DirectDisplaySurface::OnCreateQueue"); - ALOGD_IF( - TRACE, - "DirectDisplaySurface::OnCreateQueue: surface_id=%d meta_size_bytes=%zu", - surface_id(), config.meta_size_bytes); + ALOGD_IF(TRACE, + "DirectDisplaySurface::OnCreateQueue: surface_id=%d " + "user_metadata_size=%zu", + surface_id(), config.user_metadata_size); std::lock_guard<std::mutex> autolock(lock_); if (!direct_queue_) { @@ -301,6 +299,9 @@ Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue( } direct_queue_ = producer->CreateConsumerQueue(); + if (direct_queue_->metadata_size() > 0) { + metadata_.reset(new uint8_t[direct_queue_->metadata_size()]); + } auto status = RegisterQueue(direct_queue_); if (!status) { ALOGE( @@ -345,7 +346,12 @@ void DirectDisplaySurface::DequeueBuffersLocked() { while (true) { LocalHandle acquire_fence; size_t slot; - auto buffer_status = direct_queue_->Dequeue(0, &slot, &acquire_fence); + auto buffer_status = direct_queue_->Dequeue( + 0, &slot, metadata_.get(), + direct_queue_->metadata_size(), &acquire_fence); + ALOGD_IF(TRACE, + "DirectDisplaySurface::DequeueBuffersLocked: Dequeue with metadata_size: %zu", + direct_queue_->metadata_size()); if (!buffer_status) { ALOGD_IF( TRACE > 1 && buffer_status.error() == ETIMEDOUT, @@ -376,7 +382,7 @@ void DirectDisplaySurface::DequeueBuffersLocked() { } acquired_buffers_.Append( - AcquiredBuffer(buffer_consumer, std::move(acquire_fence))); + AcquiredBuffer(buffer_consumer, std::move(acquire_fence), slot)); } } @@ -463,8 +469,8 @@ Status<std::shared_ptr<DisplaySurface>> DisplaySurface::Create( if (direct) { const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id); if (trusted) { - return {std::shared_ptr<DisplaySurface>{new DirectDisplaySurface( - service, surface_id, process_id, user_id, attributes)}}; + return {std::shared_ptr<DisplaySurface>{ + new DirectDisplaySurface(service, surface_id, process_id, user_id)}}; } else { ALOGE( "DisplaySurface::Create: Direct surfaces may only be created by " @@ -474,7 +480,7 @@ Status<std::shared_ptr<DisplaySurface>> DisplaySurface::Create( } } else { return {std::shared_ptr<DisplaySurface>{new ApplicationDisplaySurface( - service, surface_id, process_id, user_id, attributes)}}; + service, surface_id, process_id, user_id)}}; } } diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h index 5cbee57bf9..c8b1a078f7 100644 --- a/libs/vr/libvrflinger/display_surface.h +++ b/libs/vr/libvrflinger/display_surface.h @@ -53,8 +53,7 @@ class DisplaySurface : public pdx::Channel { protected: DisplaySurface(DisplayService* service, SurfaceType surface_type, - int surface_id, int process_id, int user_id, - const display::SurfaceAttributes& attributes); + int surface_id, int process_id, int user_id); // Utility to retrieve a shared pointer to this channel as the desired derived // type. @@ -119,10 +118,9 @@ class DisplaySurface : public pdx::Channel { class ApplicationDisplaySurface : public DisplaySurface { public: ApplicationDisplaySurface(DisplayService* service, int surface_id, - int process_id, int user_id, - const display::SurfaceAttributes& attributes) + int process_id, int user_id) : DisplaySurface(service, SurfaceType::Application, surface_id, - process_id, user_id, attributes) {} + process_id, user_id) {} std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id); std::vector<int32_t> GetQueueIds() const override; @@ -140,11 +138,11 @@ class ApplicationDisplaySurface : public DisplaySurface { class DirectDisplaySurface : public DisplaySurface { public: DirectDisplaySurface(DisplayService* service, int surface_id, int process_id, - int user_id, - const display::SurfaceAttributes& attributes) + int user_id) : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id, - user_id, attributes), - acquired_buffers_(kMaxPostedBuffers) {} + user_id), + acquired_buffers_(kMaxPostedBuffers), + metadata_(nullptr) {} std::vector<int32_t> GetQueueIds() const override; bool IsBufferAvailable(); bool IsBufferPosted(); @@ -179,6 +177,9 @@ class DirectDisplaySurface : public DisplaySurface { RingBuffer<AcquiredBuffer> acquired_buffers_; std::shared_ptr<ConsumerQueue> direct_queue_; + + // Stores metadata when it dequeue buffers from consumer queue. + std::unique_ptr<uint8_t[]> metadata_; }; } // namespace dvr diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp index def9b7da33..fb69d5cf59 100644 --- a/libs/vr/libvrflinger/hardware_composer.cpp +++ b/libs/vr/libvrflinger/hardware_composer.cpp @@ -5,12 +5,14 @@ #include <fcntl.h> #include <log/log.h> #include <poll.h> +#include <stdint.h> #include <sync/sync.h> #include <sys/eventfd.h> #include <sys/prctl.h> #include <sys/resource.h> #include <sys/system_properties.h> #include <sys/timerfd.h> +#include <sys/types.h> #include <time.h> #include <unistd.h> #include <utils/Trace.h> @@ -28,7 +30,11 @@ #include <private/dvr/clock_ns.h> #include <private/dvr/ion_buffer.h> +using android::hardware::Return; +using android::hardware::Void; +using android::pdx::ErrorStatus; using android::pdx::LocalHandle; +using android::pdx::Status; using android::pdx::rpc::EmptyVariant; using android::pdx::rpc::IfAnyOf; @@ -42,12 +48,8 @@ namespace { const char kBacklightBrightnessSysFile[] = "/sys/class/leds/lcd-backlight/brightness"; -const char kPrimaryDisplayVSyncEventFile[] = - "/sys/class/graphics/fb0/vsync_event"; - -const char kPrimaryDisplayWaitPPEventFile[] = "/sys/class/graphics/fb0/wait_pp"; - const char kDvrPerformanceProperty[] = "sys.dvr.performance"; +const char kDvrStandaloneProperty[] = "ro.boot.vr"; const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns"; @@ -84,24 +86,33 @@ bool SetThreadPolicy(const std::string& scheduler_class, return true; } -} // anonymous namespace +// Utility to generate scoped tracers with arguments. +// TODO(eieio): Move/merge this into utils/Trace.h? +class TraceArgs { + public: + template <typename... Args> + TraceArgs(const char* format, Args&&... args) { + std::array<char, 1024> buffer; + snprintf(buffer.data(), buffer.size(), format, std::forward<Args>(args)...); + atrace_begin(ATRACE_TAG, buffer.data()); + } -// Layer static data. -Hwc2::Composer* Layer::hwc2_hidl_; -const HWCDisplayMetrics* Layer::display_metrics_; + ~TraceArgs() { atrace_end(ATRACE_TAG); } -// HardwareComposer static data; -constexpr size_t HardwareComposer::kMaxHardwareLayers; + private: + TraceArgs(const TraceArgs&) = delete; + void operator=(const TraceArgs&) = delete; +}; -HardwareComposer::HardwareComposer() - : HardwareComposer(nullptr, RequestDisplayCallback()) {} +// Macro to define a scoped tracer with arguments. Uses PASTE(x, y) macro +// defined in utils/Trace.h. +#define TRACE_FORMAT(format, ...) \ + TraceArgs PASTE(__tracer, __LINE__) { format, ##__VA_ARGS__ } + +} // anonymous namespace -HardwareComposer::HardwareComposer( - Hwc2::Composer* hwc2_hidl, RequestDisplayCallback request_display_callback) - : initialized_(false), - hwc2_hidl_(hwc2_hidl), - request_display_callback_(request_display_callback), - callbacks_(new ComposerCallback) {} +HardwareComposer::HardwareComposer() + : initialized_(false), request_display_callback_(nullptr) {} HardwareComposer::~HardwareComposer(void) { UpdatePostThreadState(PostThreadState::Quit, true); @@ -109,16 +120,21 @@ HardwareComposer::~HardwareComposer(void) { post_thread_.join(); } -bool HardwareComposer::Initialize() { +bool HardwareComposer::Initialize( + Hwc2::Composer* composer, RequestDisplayCallback request_display_callback) { if (initialized_) { ALOGE("HardwareComposer::Initialize: already initialized."); return false; } + is_standalone_device_ = property_get_bool(kDvrStandaloneProperty, false); + + request_display_callback_ = request_display_callback; + HWC::Error error = HWC::Error::None; Hwc2::Config config; - error = hwc2_hidl_->getActiveConfig(HWC_DISPLAY_PRIMARY, &config); + error = composer->getActiveConfig(HWC_DISPLAY_PRIMARY, &config); if (error != HWC::Error::None) { ALOGE("HardwareComposer: Failed to get current display config : %d", @@ -126,8 +142,8 @@ bool HardwareComposer::Initialize() { return false; } - error = - GetDisplayMetrics(HWC_DISPLAY_PRIMARY, config, &native_display_metrics_); + error = GetDisplayMetrics(composer, HWC_DISPLAY_PRIMARY, config, + &native_display_metrics_); if (error != HWC::Error::None) { ALOGE( @@ -149,8 +165,8 @@ bool HardwareComposer::Initialize() { display_transform_ = HWC_TRANSFORM_NONE; display_metrics_ = native_display_metrics_; - // Pass hwc instance and metrics to setup globals for Layer. - Layer::InitializeGlobals(hwc2_hidl_, &native_display_metrics_); + // Setup the display metrics used by all Layer instances. + Layer::SetDisplayMetrics(native_display_metrics_); post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); LOG_ALWAYS_FATAL_IF( @@ -210,15 +226,19 @@ void HardwareComposer::UpdatePostThreadState(PostThreadStateType state, } void HardwareComposer::OnPostThreadResumed() { - hwc2_hidl_->resetCommands(); + // Phones create a new composer client on resume and destroy it on pause. + // Standalones only create the composer client once and then use SetPowerMode + // to control the screen on pause/resume. + if (!is_standalone_device_ || !composer_) { + composer_.reset(new Hwc2::Composer("default")); + composer_callback_ = new ComposerCallback; + composer_->registerCallback(composer_callback_); + Layer::SetComposer(composer_.get()); + } else { + SetPowerMode(true); + } - // HIDL HWC seems to have an internal race condition. If we submit a frame too - // soon after turning on VSync we don't get any VSync signals. Give poor HWC - // implementations a chance to enable VSync before we continue. - EnableVsync(false); - std::this_thread::sleep_for(100ms); EnableVsync(true); - std::this_thread::sleep_for(100ms); // TODO(skiazyk): We need to do something about accessing this directly, // supposedly there is a backlight service on the way. @@ -233,16 +253,19 @@ void HardwareComposer::OnPostThreadResumed() { void HardwareComposer::OnPostThreadPaused() { retire_fence_fds_.clear(); - display_surfaces_.clear(); + layers_.clear(); - for (size_t i = 0; i < kMaxHardwareLayers; ++i) { - layers_[i].Reset(); + if (composer_) { + EnableVsync(false); } - active_layer_count_ = 0; - - EnableVsync(false); - hwc2_hidl_->resetCommands(); + if (!is_standalone_device_) { + composer_callback_ = nullptr; + composer_.reset(nullptr); + Layer::SetComposer(nullptr); + } else { + SetPowerMode(false); + } // Trigger target-specific performance mode change. property_set(kDvrPerformanceProperty, "idle"); @@ -252,29 +275,35 @@ HWC::Error HardwareComposer::Validate(hwc2_display_t display) { uint32_t num_types; uint32_t num_requests; HWC::Error error = - hwc2_hidl_->validateDisplay(display, &num_types, &num_requests); + composer_->validateDisplay(display, &num_types, &num_requests); if (error == HWC2_ERROR_HAS_CHANGES) { // TODO(skiazyk): We might need to inspect the requested changes first, but // so far it seems like we shouldn't ever hit a bad state. // error = hwc2_funcs_.accept_display_changes_fn_(hardware_composer_device_, // display); - error = hwc2_hidl_->acceptDisplayChanges(display); + error = composer_->acceptDisplayChanges(display); } return error; } -int32_t HardwareComposer::EnableVsync(bool enabled) { - return (int32_t)hwc2_hidl_->setVsyncEnabled( +HWC::Error HardwareComposer::EnableVsync(bool enabled) { + return composer_->setVsyncEnabled( HWC_DISPLAY_PRIMARY, (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE : HWC2_VSYNC_DISABLE)); } +HWC::Error HardwareComposer::SetPowerMode(bool active) { + HWC::PowerMode power_mode = active ? HWC::PowerMode::On : HWC::PowerMode::Off; + return composer_->setPowerMode( + HWC_DISPLAY_PRIMARY, power_mode.cast<Hwc2::IComposerClient::PowerMode>()); +} + HWC::Error HardwareComposer::Present(hwc2_display_t display) { int32_t present_fence; - HWC::Error error = hwc2_hidl_->presentDisplay(display, &present_fence); + HWC::Error error = composer_->presentDisplay(display, &present_fence); // According to the documentation, this fence is signaled at the time of // vsync/DMA for physical displays. @@ -288,20 +317,21 @@ HWC::Error HardwareComposer::Present(hwc2_display_t display) { return error; } -HWC::Error HardwareComposer::GetDisplayAttribute(hwc2_display_t display, +HWC::Error HardwareComposer::GetDisplayAttribute(Hwc2::Composer* composer, + hwc2_display_t display, hwc2_config_t config, hwc2_attribute_t attribute, int32_t* out_value) const { - return hwc2_hidl_->getDisplayAttribute( + return composer->getDisplayAttribute( display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value); } HWC::Error HardwareComposer::GetDisplayMetrics( - hwc2_display_t display, hwc2_config_t config, + Hwc2::Composer* composer, hwc2_display_t display, hwc2_config_t config, HWCDisplayMetrics* out_metrics) const { HWC::Error error; - error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_WIDTH, + error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_WIDTH, &out_metrics->width); if (error != HWC::Error::None) { ALOGE( @@ -310,7 +340,7 @@ HWC::Error HardwareComposer::GetDisplayMetrics( return error; } - error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_HEIGHT, + error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_HEIGHT, &out_metrics->height); if (error != HWC::Error::None) { ALOGE( @@ -319,7 +349,8 @@ HWC::Error HardwareComposer::GetDisplayMetrics( return error; } - error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_VSYNC_PERIOD, + error = GetDisplayAttribute(composer, display, config, + HWC2_ATTRIBUTE_VSYNC_PERIOD, &out_metrics->vsync_period_ns); if (error != HWC::Error::None) { ALOGE( @@ -328,7 +359,7 @@ HWC::Error HardwareComposer::GetDisplayMetrics( return error; } - error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_X, + error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_DPI_X, &out_metrics->dpi.x); if (error != HWC::Error::None) { ALOGE( @@ -337,7 +368,7 @@ HWC::Error HardwareComposer::GetDisplayMetrics( return error; } - error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_Y, + error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_DPI_Y, &out_metrics->dpi.y); if (error != HWC::Error::None) { ALOGE( @@ -360,10 +391,10 @@ std::string HardwareComposer::Dump() { << std::endl; stream << "Post thread resumed: " << post_thread_resumed_ << std::endl; - stream << "Active layers: " << active_layer_count_ << std::endl; + stream << "Active layers: " << layers_.size() << std::endl; stream << std::endl; - for (size_t i = 0; i < active_layer_count_; i++) { + for (size_t i = 0; i < layers_.size(); i++) { stream << "Layer " << i << ":"; stream << " type=" << layers_[i].GetCompositionType().to_string(); stream << " surface_id=" << layers_[i].GetSurfaceId(); @@ -374,7 +405,7 @@ std::string HardwareComposer::Dump() { if (post_thread_resumed_) { stream << "Hardware Composer Debug Info:" << std::endl; - stream << hwc2_hidl_->dumpDebugInfo(); + stream << composer_->dumpDebugInfo(); } return stream.str(); @@ -384,8 +415,8 @@ void HardwareComposer::PostLayers() { ATRACE_NAME("HardwareComposer::PostLayers"); // Setup the hardware composer layers with current buffers. - for (size_t i = 0; i < active_layer_count_; i++) { - layers_[i].Prepare(); + for (auto& layer : layers_) { + layer.Prepare(); } HWC::Error error = Validate(HWC_DISPLAY_PRIMARY); @@ -407,20 +438,18 @@ void HardwareComposer::PostLayers() { retire_fence_fds_.erase(retire_fence_fds_.begin()); } - const bool is_frame_pending = IsFramePendingInDriver(); - const bool is_fence_pending = retire_fence_fds_.size() > + const bool is_fence_pending = static_cast<int32_t>(retire_fence_fds_.size()) > post_thread_config_.allowed_pending_fence_count; - if (is_fence_pending || is_frame_pending) { + if (is_fence_pending) { ATRACE_INT("frame_skip_count", ++frame_skip_count_); - ALOGW_IF(is_frame_pending, "Warning: frame already queued, dropping frame"); ALOGW_IF(is_fence_pending, "Warning: dropping a frame to catch up with HWC (pending = %zd)", retire_fence_fds_.size()); - for (size_t i = 0; i < active_layer_count_; i++) { - layers_[i].Drop(); + for (auto& layer : layers_) { + layer.Drop(); } return; } else { @@ -430,7 +459,7 @@ void HardwareComposer::PostLayers() { } #if TRACE > 1 - for (size_t i = 0; i < active_layer_count_; i++) { + for (size_t i = 0; i < layers_.size(); i++) { ALOGI("HardwareComposer::PostLayers: layer=%zu buffer_id=%d composition=%s", i, layers_[i].GetBufferId(), layers_[i].GetCompositionType().to_string().c_str()); @@ -446,18 +475,18 @@ void HardwareComposer::PostLayers() { std::vector<Hwc2::Layer> out_layers; std::vector<int> out_fences; - error = hwc2_hidl_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers, - &out_fences); + error = composer_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers, + &out_fences); ALOGE_IF(error != HWC::Error::None, "HardwareComposer::PostLayers: Failed to get release fences: %s", error.to_string().c_str()); - // Perform post-frame bookkeeping. Unused layers are a no-op. + // Perform post-frame bookkeeping. uint32_t num_elements = out_layers.size(); for (size_t i = 0; i < num_elements; ++i) { - for (size_t j = 0; j < active_layer_count_; ++j) { - if (layers_[j].GetLayerHandle() == out_layers[i]) { - layers_[j].Finish(out_fences[i]); + for (auto& layer : layers_) { + if (layer.GetLayerHandle() == out_layers[i]) { + layer.Finish(out_fences[i]); } } } @@ -473,7 +502,7 @@ void HardwareComposer::SetDisplaySurfaces( pending_surfaces_ = std::move(surfaces); } - if (request_display_callback_) + if (request_display_callback_ && (!is_standalone_device_ || !composer_)) request_display_callback_(!display_idle); // Set idle state based on whether there are any surfaces to handle. @@ -546,7 +575,7 @@ void HardwareComposer::UpdateConfigBuffer() { } int HardwareComposer::PostThreadPollInterruptible( - const pdx::LocalHandle& event_fd, int requested_events) { + const pdx::LocalHandle& event_fd, int requested_events, int timeout_ms) { pollfd pfd[2] = { { .fd = event_fd.Get(), @@ -561,7 +590,7 @@ int HardwareComposer::PostThreadPollInterruptible( }; int ret, error; do { - ret = poll(pfd, 2, -1); + ret = poll(pfd, 2, timeout_ms); error = errno; ALOGW_IF(ret < 0, "HardwareComposer::PostThreadPollInterruptible: Error during " @@ -571,167 +600,40 @@ int HardwareComposer::PostThreadPollInterruptible( if (ret < 0) { return -error; + } else if (ret == 0) { + return -ETIMEDOUT; } else if (pfd[0].revents != 0) { return 0; } else if (pfd[1].revents != 0) { - ALOGI("VrHwcPost thread interrupted"); + ALOGI("VrHwcPost thread interrupted: revents=%x", pfd[1].revents); return kPostThreadInterrupted; } else { return 0; } } -// Reads the value of the display driver wait_pingpong state. Returns 0 or 1 -// (the value of the state) on success or a negative error otherwise. -// TODO(eieio): This is pretty driver specific, this should be moved to a -// separate class eventually. -int HardwareComposer::ReadWaitPPState() { - // Gracefully handle when the kernel does not support this feature. - if (!primary_display_wait_pp_fd_) - return 0; - - const int wait_pp_fd = primary_display_wait_pp_fd_.Get(); - int ret, error; - - ret = lseek(wait_pp_fd, 0, SEEK_SET); - if (ret < 0) { - error = errno; - ALOGE("HardwareComposer::ReadWaitPPState: Failed to seek wait_pp fd: %s", - strerror(error)); - return -error; - } - - char data = -1; - ret = read(wait_pp_fd, &data, sizeof(data)); - if (ret < 0) { - error = errno; - ALOGE("HardwareComposer::ReadWaitPPState: Failed to read wait_pp state: %s", - strerror(error)); - return -error; - } - - switch (data) { - case '0': - return 0; - case '1': - return 1; - default: - ALOGE( - "HardwareComposer::ReadWaitPPState: Unexpected value for wait_pp: %d", - data); - return -EINVAL; - } -} - -// Reads the timestamp of the last vsync from the display driver. -// TODO(eieio): This is pretty driver specific, this should be moved to a -// separate class eventually. -int HardwareComposer::ReadVSyncTimestamp(int64_t* timestamp) { - const int event_fd = primary_display_vsync_event_fd_.Get(); - int ret, error; - - // The driver returns data in the form "VSYNC=<timestamp ns>". - std::array<char, 32> data; - data.fill('\0'); - - // Seek back to the beginning of the event file. - ret = lseek(event_fd, 0, SEEK_SET); - if (ret < 0) { - error = errno; - ALOGE( - "HardwareComposer::ReadVSyncTimestamp: Failed to seek vsync event fd: " - "%s", - strerror(error)); - return -error; - } - - // Read the vsync event timestamp. - ret = read(event_fd, data.data(), data.size()); - if (ret < 0) { - error = errno; - ALOGE_IF( - error != EAGAIN, - "HardwareComposer::ReadVSyncTimestamp: Error while reading timestamp: " - "%s", - strerror(error)); - return -error; - } - - ret = sscanf(data.data(), "VSYNC=%" PRIu64, - reinterpret_cast<uint64_t*>(timestamp)); - if (ret < 0) { - error = errno; - ALOGE( - "HardwareComposer::ReadVSyncTimestamp: Error while parsing timestamp: " - "%s", - strerror(error)); - return -error; - } - - return 0; -} - -// Blocks until the next vsync event is signaled by the display driver. -// TODO(eieio): This is pretty driver specific, this should be moved to a -// separate class eventually. -int HardwareComposer::BlockUntilVSync() { - // Vsync is signaled by POLLPRI on the fb vsync node. - return PostThreadPollInterruptible(primary_display_vsync_event_fd_, POLLPRI); +Status<int64_t> HardwareComposer::GetVSyncTime() { + auto status = composer_callback_->GetVsyncTime(HWC_DISPLAY_PRIMARY); + ALOGE_IF(!status, + "HardwareComposer::GetVSyncTime: Failed to get vsync timestamp: %s", + status.GetErrorMessage().c_str()); + return status; } // Waits for the next vsync and returns the timestamp of the vsync event. If // vsync already passed since the last call, returns the latest vsync timestamp -// instead of blocking. This method updates the last_vsync_timeout_ in the -// process. -// -// TODO(eieio): This is pretty driver specific, this should be moved to a -// separate class eventually. -int HardwareComposer::WaitForVSync(int64_t* timestamp) { - int error; - - // Get the current timestamp and decide what to do. - while (true) { - int64_t current_vsync_timestamp; - error = ReadVSyncTimestamp(¤t_vsync_timestamp); - if (error < 0 && error != -EAGAIN) - return error; - - if (error == -EAGAIN) { - // Vsync was turned off, wait for the next vsync event. - error = BlockUntilVSync(); - if (error < 0 || error == kPostThreadInterrupted) - return error; - - // Try again to get the timestamp for this new vsync interval. - continue; - } - - // Check that we advanced to a later vsync interval. - if (TimestampGT(current_vsync_timestamp, last_vsync_timestamp_)) { - *timestamp = last_vsync_timestamp_ = current_vsync_timestamp; - return 0; - } - - // See how close we are to the next expected vsync. If we're within 1ms, - // sleep for 1ms and try again. - const int64_t ns_per_frame = display_metrics_.vsync_period_ns; - const int64_t threshold_ns = 1000000; // 1ms - - const int64_t next_vsync_est = last_vsync_timestamp_ + ns_per_frame; - const int64_t distance_to_vsync_est = next_vsync_est - GetSystemClockNs(); - - if (distance_to_vsync_est > threshold_ns) { - // Wait for vsync event notification. - error = BlockUntilVSync(); - if (error < 0 || error == kPostThreadInterrupted) - return error; - } else { - // Sleep for a short time (1 millisecond) before retrying. - error = SleepUntil(GetSystemClockNs() + threshold_ns); - if (error < 0 || error == kPostThreadInterrupted) - return error; - } +// instead of blocking. +Status<int64_t> HardwareComposer::WaitForVSync() { + const int64_t predicted_vsync_time = + last_vsync_timestamp_ + + display_metrics_.vsync_period_ns * vsync_prediction_interval_; + const int error = SleepUntil(predicted_vsync_time); + if (error < 0) { + ALOGE("HardwareComposer::WaifForVSync:: Failed to sleep: %s", + strerror(-error)); + return error; } + return {predicted_vsync_time}; } int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) { @@ -749,7 +651,8 @@ int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) { return -error; } - return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN); + return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN, + /*timeout_ms*/ -1); } void HardwareComposer::PostThread() { @@ -772,24 +675,6 @@ void HardwareComposer::PostThread() { strerror(errno)); #endif // ENABLE_BACKLIGHT_BRIGHTNESS - // Open the vsync event node for the primary display. - // TODO(eieio): Move this into a platform-specific class. - primary_display_vsync_event_fd_ = - LocalHandle(kPrimaryDisplayVSyncEventFile, O_RDONLY); - ALOGE_IF(!primary_display_vsync_event_fd_, - "HardwareComposer: Failed to open vsync event node for primary " - "display: %s", - strerror(errno)); - - // Open the wait pingpong status node for the primary display. - // TODO(eieio): Move this into a platform-specific class. - primary_display_wait_pp_fd_ = - LocalHandle(kPrimaryDisplayWaitPPEventFile, O_RDONLY); - ALOGW_IF( - !primary_display_wait_pp_fd_, - "HardwareComposer: Failed to open wait_pp node for primary display: %s", - strerror(errno)); - // Create a timerfd based on CLOCK_MONOTINIC. vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0)); LOG_ALWAYS_FATAL_IF( @@ -854,26 +739,41 @@ void HardwareComposer::PostThread() { thread_policy_setup = SetThreadPolicy("graphics:high", "/system/performance"); } + + // Initialize the last vsync timestamp with the current time. The + // predictor below uses this time + the vsync interval in absolute time + // units for the initial delay. Once the driver starts reporting vsync the + // predictor will sync up with the real vsync. + last_vsync_timestamp_ = GetSystemClockNs(); } int64_t vsync_timestamp = 0; { - std::array<char, 128> buf; - snprintf(buf.data(), buf.size(), "wait_vsync|vsync=%d|", - vsync_count_ + 1); - ATRACE_NAME(buf.data()); + TRACE_FORMAT("wait_vsync|vsync=%u;last_timestamp=%" PRId64 + ";prediction_interval=%d|", + vsync_count_ + 1, last_vsync_timestamp_, + vsync_prediction_interval_); - const int error = WaitForVSync(&vsync_timestamp); + auto status = WaitForVSync(); ALOGE_IF( - error < 0, + !status, "HardwareComposer::PostThread: Failed to wait for vsync event: %s", - strerror(-error)); - // Don't bother processing this frame if a pause was requested - if (error == kPostThreadInterrupted) + status.GetErrorMessage().c_str()); + + // If there was an error either sleeping was interrupted due to pausing or + // there was an error getting the latest timestamp. + if (!status) continue; + + // Predicted vsync timestamp for this interval. This is stable because we + // use absolute time for the wakeup timer. + vsync_timestamp = status.get(); } - ++vsync_count_; + // Advance the vsync counter only if the system is keeping up with hardware + // vsync to give clients an indication of the delays. + if (vsync_prediction_interval_ == 1) + ++vsync_count_; const bool layer_config_changed = UpdateLayerConfig(); @@ -923,6 +823,38 @@ void HardwareComposer::PostThread() { } } + { + auto status = GetVSyncTime(); + if (!status) { + ALOGE("HardwareComposer::PostThread: Failed to get VSYNC time: %s", + status.GetErrorMessage().c_str()); + } + + // If we failed to read vsync there might be a problem with the driver. + // Since there's nothing we can do just behave as though we didn't get an + // updated vsync time and let the prediction continue. + const int64_t current_vsync_timestamp = + status ? status.get() : last_vsync_timestamp_; + + const bool vsync_delayed = + last_vsync_timestamp_ == current_vsync_timestamp; + ATRACE_INT("vsync_delayed", vsync_delayed); + + // If vsync was delayed advance the prediction interval and allow the + // fence logic in PostLayers() to skip the frame. + if (vsync_delayed) { + ALOGW( + "HardwareComposer::PostThread: VSYNC timestamp did not advance " + "since last frame: timestamp=%" PRId64 " prediction_interval=%d", + current_vsync_timestamp, vsync_prediction_interval_); + vsync_prediction_interval_++; + } else { + // We have an updated vsync timestamp, reset the prediction interval. + last_vsync_timestamp_ = current_vsync_timestamp; + vsync_prediction_interval_ = 1; + } + } + PostLayers(); } } @@ -941,37 +873,60 @@ bool HardwareComposer::UpdateLayerConfig() { ATRACE_NAME("UpdateLayerConfig_HwLayers"); - display_surfaces_.clear(); + // Sort the new direct surface list by z-order to determine the relative order + // of the surfaces. This relative order is used for the HWC z-order value to + // insulate VrFlinger and HWC z-order semantics from each other. + std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) { + return a->z_order() < b->z_order(); + }); - Layer* target_layer; - size_t layer_index; - for (layer_index = 0; - layer_index < std::min(surfaces.size(), kMaxHardwareLayers); - layer_index++) { + // Prepare a new layer stack, pulling in layers from the previous + // layer stack that are still active and updating their attributes. + std::vector<Layer> layers; + size_t layer_index = 0; + for (const auto& surface : surfaces) { // The bottom layer is opaque, other layers blend. HWC::BlendMode blending = layer_index == 0 ? HWC::BlendMode::None : HWC::BlendMode::Coverage; - layers_[layer_index].Setup(surfaces[layer_index], blending, - display_transform_, HWC::Composition::Device, - layer_index); - display_surfaces_.push_back(surfaces[layer_index]); - } - // Clear unused layers. - for (size_t i = layer_index; i < kMaxHardwareLayers; i++) - layers_[i].Reset(); + // Try to find a layer for this surface in the set of active layers. + auto search = + std::lower_bound(layers_.begin(), layers_.end(), surface->surface_id()); + const bool found = search != layers_.end() && + search->GetSurfaceId() == surface->surface_id(); + if (found) { + // Update the attributes of the layer that may have changed. + search->SetBlending(blending); + search->SetZOrder(layer_index); // Relative z-order. + + // Move the existing layer to the new layer set and remove the empty layer + // object from the current set. + layers.push_back(std::move(*search)); + layers_.erase(search); + } else { + // Insert a layer for the new surface. + layers.emplace_back(surface, blending, display_transform_, + HWC::Composition::Device, layer_index); + } + + ALOGI_IF( + TRACE, + "HardwareComposer::UpdateLayerConfig: layer_index=%zu surface_id=%d", + layer_index, layers[layer_index].GetSurfaceId()); - active_layer_count_ = layer_index; - ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers", - active_layer_count_); + layer_index++; + } + + // Sort the new layer stack by ascending surface id. + std::sort(layers.begin(), layers.end()); - // Any surfaces left over could not be assigned a hardware layer and will - // not be displayed. - ALOGW_IF(surfaces.size() != display_surfaces_.size(), - "HardwareComposer::UpdateLayerConfig: More surfaces than layers: " - "pending_surfaces=%zu display_surfaces=%zu", - surfaces.size(), display_surfaces_.size()); + // Replace the previous layer set with the new layer set. The destructor of + // the previous set will clean up the remaining Layers that are not moved to + // the new layer set. + layers_ = std::move(layers); + ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers", + layers_.size()); return true; } @@ -979,47 +934,129 @@ void HardwareComposer::SetVSyncCallback(VSyncCallback callback) { vsync_callback_ = callback; } -void HardwareComposer::HwcRefresh(hwc2_callback_data_t /*data*/, - hwc2_display_t /*display*/) { - // TODO(eieio): implement invalidate callbacks. +void HardwareComposer::SetBacklightBrightness(int brightness) { + if (backlight_brightness_fd_) { + std::array<char, 32> text; + const int length = snprintf(text.data(), text.size(), "%d", brightness); + write(backlight_brightness_fd_.Get(), text.data(), length); + } } -void HardwareComposer::HwcVSync(hwc2_callback_data_t /*data*/, - hwc2_display_t /*display*/, - int64_t /*timestamp*/) { - ATRACE_NAME(__PRETTY_FUNCTION__); - // Intentionally empty. HWC may require a callback to be set to enable vsync - // signals. We bypass this callback thread by monitoring the vsync event - // directly, but signals still need to be enabled. -} +Return<void> HardwareComposer::ComposerCallback::onHotplug( + Hwc2::Display display, IComposerCallback::Connection /*conn*/) { + // See if the driver supports the vsync_event node in sysfs. + if (display < HWC_NUM_PHYSICAL_DISPLAY_TYPES && + !displays_[display].driver_vsync_event_fd) { + std::array<char, 1024> buffer; + snprintf(buffer.data(), buffer.size(), + "/sys/class/graphics/fb%" PRIu64 "/vsync_event", display); + if (LocalHandle handle{buffer.data(), O_RDONLY}) { + ALOGI( + "HardwareComposer::ComposerCallback::onHotplug: Driver supports " + "vsync_event node for display %" PRIu64, + display); + displays_[display].driver_vsync_event_fd = std::move(handle); + } else { + ALOGI( + "HardwareComposer::ComposerCallback::onHotplug: Driver does not " + "support vsync_event node for display %" PRIu64, + display); + } + } -void HardwareComposer::HwcHotplug(hwc2_callback_data_t /*callbackData*/, - hwc2_display_t /*display*/, - hwc2_connection_t /*connected*/) { - // TODO(eieio): implement display hotplug callbacks. + return Void(); } -void HardwareComposer::OnHardwareComposerRefresh() { - // TODO(steventhomas): Handle refresh. +Return<void> HardwareComposer::ComposerCallback::onRefresh( + Hwc2::Display /*display*/) { + return hardware::Void(); } -void HardwareComposer::SetBacklightBrightness(int brightness) { - if (backlight_brightness_fd_) { - std::array<char, 32> text; - const int length = snprintf(text.data(), text.size(), "%d", brightness); - write(backlight_brightness_fd_.Get(), text.data(), length); +Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display, + int64_t timestamp) { + TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|", + display, timestamp); + if (display < HWC_NUM_PHYSICAL_DISPLAY_TYPES) { + displays_[display].callback_vsync_timestamp = timestamp; + } else { + ALOGW( + "HardwareComposer::ComposerCallback::onVsync: Received vsync on " + "non-physical display: display=%" PRId64, + display); } + return Void(); } -void Layer::InitializeGlobals(Hwc2::Composer* hwc2_hidl, - const HWCDisplayMetrics* metrics) { - hwc2_hidl_ = hwc2_hidl; - display_metrics_ = metrics; +Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime( + Hwc2::Display display) { + if (display >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) { + ALOGE( + "HardwareComposer::ComposerCallback::GetVsyncTime: Invalid physical " + "display requested: display=%" PRIu64, + display); + return ErrorStatus(EINVAL); + } + + // See if the driver supports direct vsync events. + LocalHandle& event_fd = displays_[display].driver_vsync_event_fd; + if (!event_fd) { + // Fall back to returning the last timestamp returned by the vsync + // callback. + std::lock_guard<std::mutex> autolock(vsync_mutex_); + return displays_[display].callback_vsync_timestamp; + } + + // When the driver supports the vsync_event sysfs node we can use it to + // determine the latest vsync timestamp, even if the HWC callback has been + // delayed. + + // The driver returns data in the form "VSYNC=<timestamp ns>". + std::array<char, 32> data; + data.fill('\0'); + + // Seek back to the beginning of the event file. + int ret = lseek(event_fd.Get(), 0, SEEK_SET); + if (ret < 0) { + const int error = errno; + ALOGE( + "HardwareComposer::ComposerCallback::GetVsyncTime: Failed to seek " + "vsync event fd: %s", + strerror(error)); + return ErrorStatus(error); + } + + // Read the vsync event timestamp. + ret = read(event_fd.Get(), data.data(), data.size()); + if (ret < 0) { + const int error = errno; + ALOGE_IF(error != EAGAIN, + "HardwareComposer::ComposerCallback::GetVsyncTime: Error " + "while reading timestamp: %s", + strerror(error)); + return ErrorStatus(error); + } + + int64_t timestamp; + ret = sscanf(data.data(), "VSYNC=%" PRIu64, + reinterpret_cast<uint64_t*>(×tamp)); + if (ret < 0) { + const int error = errno; + ALOGE( + "HardwareComposer::ComposerCallback::GetVsyncTime: Error while " + "parsing timestamp: %s", + strerror(error)); + return ErrorStatus(error); + } + + return {timestamp}; } +Hwc2::Composer* Layer::composer_{nullptr}; +HWCDisplayMetrics Layer::display_metrics_{0, 0, {0, 0}, 0}; + void Layer::Reset() { - if (hwc2_hidl_ != nullptr && hardware_composer_layer_) { - hwc2_hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_); + if (hardware_composer_layer_) { + composer_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_); hardware_composer_layer_ = 0; } @@ -1031,41 +1068,74 @@ void Layer::Reset() { source_ = EmptyVariant{}; acquire_fence_.Close(); surface_rect_functions_applied_ = false; + pending_visibility_settings_ = true; + cached_buffer_map_.clear(); } -void Layer::Setup(const std::shared_ptr<DirectDisplaySurface>& surface, - HWC::BlendMode blending, HWC::Transform transform, - HWC::Composition composition_type, size_t z_order) { - Reset(); - z_order_ = z_order; - blending_ = blending; - transform_ = transform; - composition_type_ = HWC::Composition::Invalid; - target_composition_type_ = composition_type; - source_ = SourceSurface{surface}; +Layer::Layer(const std::shared_ptr<DirectDisplaySurface>& surface, + HWC::BlendMode blending, HWC::Transform transform, + HWC::Composition composition_type, size_t z_order) + : z_order_{z_order}, + blending_{blending}, + transform_{transform}, + target_composition_type_{composition_type}, + source_{SourceSurface{surface}} { CommonLayerSetup(); } -void Layer::Setup(const std::shared_ptr<IonBuffer>& buffer, - HWC::BlendMode blending, HWC::Transform transform, - HWC::Composition composition_type, size_t z_order) { - Reset(); - z_order_ = z_order; - blending_ = blending; - transform_ = transform; - composition_type_ = HWC::Composition::Invalid; - target_composition_type_ = composition_type; - source_ = SourceBuffer{buffer}; +Layer::Layer(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending, + HWC::Transform transform, HWC::Composition composition_type, + size_t z_order) + : z_order_{z_order}, + blending_{blending}, + transform_{transform}, + target_composition_type_{composition_type}, + source_{SourceBuffer{buffer}} { CommonLayerSetup(); } +Layer::~Layer() { Reset(); } + +Layer::Layer(Layer&& other) { *this = std::move(other); } + +Layer& Layer::operator=(Layer&& other) { + if (this != &other) { + Reset(); + using std::swap; + swap(hardware_composer_layer_, other.hardware_composer_layer_); + swap(z_order_, other.z_order_); + swap(blending_, other.blending_); + swap(transform_, other.transform_); + swap(composition_type_, other.composition_type_); + swap(target_composition_type_, other.target_composition_type_); + swap(source_, other.source_); + swap(acquire_fence_, other.acquire_fence_); + swap(surface_rect_functions_applied_, + other.surface_rect_functions_applied_); + swap(pending_visibility_settings_, other.pending_visibility_settings_); + swap(cached_buffer_map_, other.cached_buffer_map_); + } + return *this; +} + void Layer::UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer) { if (source_.is<SourceBuffer>()) std::get<SourceBuffer>(source_) = {buffer}; } -void Layer::SetBlending(HWC::BlendMode blending) { blending_ = blending; } -void Layer::SetZOrder(size_t z_order) { z_order_ = z_order; } +void Layer::SetBlending(HWC::BlendMode blending) { + if (blending_ != blending) { + blending_ = blending; + pending_visibility_settings_ = true; + } +} + +void Layer::SetZOrder(size_t z_order) { + if (z_order_ != z_order) { + z_order_ = z_order; + pending_visibility_settings_ = true; + } +} IonBuffer* Layer::GetBuffer() { struct Visitor { @@ -1076,93 +1146,108 @@ IonBuffer* Layer::GetBuffer() { return source_.Visit(Visitor{}); } -void Layer::UpdateLayerSettings() { - if (!IsLayerSetup()) { - ALOGE( - "HardwareComposer::Layer::UpdateLayerSettings: Attempt to update " - "unused Layer!"); - return; +void Layer::UpdateVisibilitySettings() { + if (pending_visibility_settings_) { + pending_visibility_settings_ = false; + + HWC::Error error; + hwc2_display_t display = HWC_DISPLAY_PRIMARY; + + error = composer_->setLayerBlendMode( + display, hardware_composer_layer_, + blending_.cast<Hwc2::IComposerClient::BlendMode>()); + ALOGE_IF(error != HWC::Error::None, + "Layer::UpdateLayerSettings: Error setting layer blend mode: %s", + error.to_string().c_str()); + + error = + composer_->setLayerZOrder(display, hardware_composer_layer_, z_order_); + ALOGE_IF(error != HWC::Error::None, + "Layer::UpdateLayerSettings: Error setting z_ order: %s", + error.to_string().c_str()); } +} +void Layer::UpdateLayerSettings() { HWC::Error error; hwc2_display_t display = HWC_DISPLAY_PRIMARY; - error = hwc2_hidl_->setLayerCompositionType( - display, hardware_composer_layer_, - composition_type_.cast<Hwc2::IComposerClient::Composition>()); - ALOGE_IF( - error != HWC::Error::None, - "Layer::UpdateLayerSettings: Error setting layer composition type: %s", - error.to_string().c_str()); - - error = hwc2_hidl_->setLayerBlendMode( - display, hardware_composer_layer_, - blending_.cast<Hwc2::IComposerClient::BlendMode>()); - ALOGE_IF(error != HWC::Error::None, - "Layer::UpdateLayerSettings: Error setting layer blend mode: %s", - error.to_string().c_str()); + UpdateVisibilitySettings(); // TODO(eieio): Use surface attributes or some other mechanism to control // the layer display frame. - error = hwc2_hidl_->setLayerDisplayFrame( + error = composer_->setLayerDisplayFrame( display, hardware_composer_layer_, - {0, 0, display_metrics_->width, display_metrics_->height}); + {0, 0, display_metrics_.width, display_metrics_.height}); ALOGE_IF(error != HWC::Error::None, "Layer::UpdateLayerSettings: Error setting layer display frame: %s", error.to_string().c_str()); - error = hwc2_hidl_->setLayerVisibleRegion( + error = composer_->setLayerVisibleRegion( display, hardware_composer_layer_, - {{0, 0, display_metrics_->width, display_metrics_->height}}); + {{0, 0, display_metrics_.width, display_metrics_.height}}); ALOGE_IF(error != HWC::Error::None, "Layer::UpdateLayerSettings: Error setting layer visible region: %s", error.to_string().c_str()); error = - hwc2_hidl_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f); + composer_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f); ALOGE_IF(error != HWC::Error::None, "Layer::UpdateLayerSettings: Error setting layer plane alpha: %s", error.to_string().c_str()); - - error = - hwc2_hidl_->setLayerZOrder(display, hardware_composer_layer_, z_order_); - ALOGE_IF(error != HWC::Error::None, - "Layer::UpdateLayerSettings: Error setting z_ order: %s", - error.to_string().c_str()); } void Layer::CommonLayerSetup() { HWC::Error error = - hwc2_hidl_->createLayer(HWC_DISPLAY_PRIMARY, &hardware_composer_layer_); - ALOGE_IF( - error != HWC::Error::None, - "Layer::CommonLayerSetup: Failed to create layer on primary display: %s", - error.to_string().c_str()); + composer_->createLayer(HWC_DISPLAY_PRIMARY, &hardware_composer_layer_); + ALOGE_IF(error != HWC::Error::None, + "Layer::CommonLayerSetup: Failed to create layer on primary " + "display: %s", + error.to_string().c_str()); UpdateLayerSettings(); } +bool Layer::CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id) { + auto search = cached_buffer_map_.find(slot); + if (search != cached_buffer_map_.end() && search->second == buffer_id) + return true; + + // Assign or update the buffer slot. + if (buffer_id >= 0) + cached_buffer_map_[slot] = buffer_id; + return false; +} + void Layer::Prepare() { - int right, bottom; + int right, bottom, id; sp<GraphicBuffer> handle; + std::size_t slot; // Acquire the next buffer according to the type of source. IfAnyOf<SourceSurface, SourceBuffer>::Call(&source_, [&](auto& source) { - std::tie(right, bottom, handle, acquire_fence_) = source.Acquire(); + std::tie(right, bottom, id, handle, acquire_fence_, slot) = + source.Acquire(); }); - // When a layer is first setup there may be some time before the first buffer - // arrives. Setup the HWC layer as a solid color to stall for time until the - // first buffer arrives. Once the first buffer arrives there will always be a - // buffer for the frame even if it is old. + TRACE_FORMAT("Layer::Prepare|buffer_id=%d;slot=%zu|", id, slot); + + // Update any visibility (blending, z-order) changes that occurred since + // last prepare. + UpdateVisibilitySettings(); + + // When a layer is first setup there may be some time before the first + // buffer arrives. Setup the HWC layer as a solid color to stall for time + // until the first buffer arrives. Once the first buffer arrives there will + // always be a buffer for the frame even if it is old. if (!handle.get()) { if (composition_type_ == HWC::Composition::Invalid) { composition_type_ = HWC::Composition::SolidColor; - hwc2_hidl_->setLayerCompositionType( + composer_->setLayerCompositionType( HWC_DISPLAY_PRIMARY, hardware_composer_layer_, composition_type_.cast<Hwc2::IComposerClient::Composition>()); Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0}; - hwc2_hidl_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_, - layer_color); + composer_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_, + layer_color); } else { // The composition type is already set. Nothing else to do until a // buffer arrives. @@ -1170,15 +1255,20 @@ void Layer::Prepare() { } else { if (composition_type_ != target_composition_type_) { composition_type_ = target_composition_type_; - hwc2_hidl_->setLayerCompositionType( + composer_->setLayerCompositionType( HWC_DISPLAY_PRIMARY, hardware_composer_layer_, composition_type_.cast<Hwc2::IComposerClient::Composition>()); } + // See if the HWC cache already has this buffer. + const bool cached = CheckAndUpdateCachedBuffer(slot, id); + if (cached) + handle = nullptr; + HWC::Error error{HWC::Error::None}; - error = hwc2_hidl_->setLayerBuffer(HWC_DISPLAY_PRIMARY, - hardware_composer_layer_, 0, handle, - acquire_fence_.Get()); + error = + composer_->setLayerBuffer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_, + slot, handle, acquire_fence_.Get()); ALOGE_IF(error != HWC::Error::None, "Layer::Prepare: Error setting layer buffer: %s", @@ -1187,9 +1277,9 @@ void Layer::Prepare() { if (!surface_rect_functions_applied_) { const float float_right = right; const float float_bottom = bottom; - error = hwc2_hidl_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY, - hardware_composer_layer_, - {0, 0, float_right, float_bottom}); + error = composer_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY, + hardware_composer_layer_, + {0, 0, float_right, float_bottom}); ALOGE_IF(error != HWC::Error::None, "Layer::Prepare: Error setting layer source crop: %s", diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h index a0c50e14d8..7010db9630 100644 --- a/libs/vr/libvrflinger/hardware_composer.h +++ b/libs/vr/libvrflinger/hardware_composer.h @@ -52,15 +52,7 @@ struct HWCDisplayMetrics { // source supplying buffers for the layer's contents. class Layer { public: - Layer() {} - - // Sets up the global state used by all Layer instances. This must be called - // before using any Layer methods. - static void InitializeGlobals(Hwc2::Composer* hwc2_hidl, - const HWCDisplayMetrics* metrics); - - // Releases any shared pointers and fence handles held by this instance. - void Reset(); + Layer() = default; // Sets up the layer to use a display surface as its content source. The Layer // automatically handles ACQUIRE/RELEASE phases for the surface's buffer train @@ -71,9 +63,9 @@ class Layer { // |composition_type| receives either HWC_FRAMEBUFFER for most layers or // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing). // |index| is the index of this surface in the DirectDisplaySurface array. - void Setup(const std::shared_ptr<DirectDisplaySurface>& surface, - HWC::BlendMode blending, HWC::Transform transform, - HWC::Composition composition_type, size_t z_roder); + Layer(const std::shared_ptr<DirectDisplaySurface>& surface, + HWC::BlendMode blending, HWC::Transform transform, + HWC::Composition composition_type, size_t z_roder); // Sets up the layer to use a direct buffer as its content source. No special // handling of the buffer is performed; responsibility for updating or @@ -83,9 +75,17 @@ class Layer { // |transform| receives HWC_TRANSFORM_* values. // |composition_type| receives either HWC_FRAMEBUFFER for most layers or // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing). - void Setup(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending, - HWC::Transform transform, HWC::Composition composition_type, - size_t z_order); + Layer(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending, + HWC::Transform transform, HWC::Composition composition_type, + size_t z_order); + + Layer(Layer&&); + Layer& operator=(Layer&&); + + ~Layer(); + + // Releases any shared pointers and fence handles held by this instance. + void Reset(); // Layers that use a direct IonBuffer should call this each frame to update // which buffer will be used for the next PostLayers. @@ -120,9 +120,6 @@ class Layer { HWC::Layer GetLayerHandle() const { return hardware_composer_layer_; } bool IsLayerSetup() const { return !source_.empty(); } - // Applies all of the settings to this layer using the hwc functions - void UpdateLayerSettings(); - int GetSurfaceId() const { int surface_id = -1; pdx::rpc::IfAnyOf<SourceSurface>::Call( @@ -141,11 +138,47 @@ class Layer { return buffer_id; } + // Compares Layers by surface id. + bool operator<(const Layer& other) const { + return GetSurfaceId() < other.GetSurfaceId(); + } + bool operator<(int surface_id) const { return GetSurfaceId() < surface_id; } + + // Sets the composer instance used by all Layer instances. + static void SetComposer(Hwc2::Composer* composer) { composer_ = composer; } + + // Sets the display metrics used by all Layer instances. + static void SetDisplayMetrics(HWCDisplayMetrics display_metrics) { + display_metrics_ = display_metrics; + } + private: void CommonLayerSetup(); - static Hwc2::Composer* hwc2_hidl_; - static const HWCDisplayMetrics* display_metrics_; + // Applies all of the settings to this layer using the hwc functions + void UpdateLayerSettings(); + + // Applies visibility settings that may have changed. + void UpdateVisibilitySettings(); + + // Checks whether the buffer, given by id, is associated with the given slot + // in the HWC buffer cache. If the slot is not associated with the given + // buffer the cache is updated to establish the association and the buffer + // should be sent to HWC using setLayerBuffer. Returns true if the association + // was already established, false if not. A buffer_id of -1 is never + // associated and always returns false. + bool CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id); + + // Composer instance shared by all instances of Layer. This must be set + // whenever a new instance of the Composer is created. This may be set to + // nullptr as long as there are no instances of Layer that might need to use + // it. + static Hwc2::Composer* composer_; + + // Display metrics shared by all instances of Layer. This must be set at least + // once during VrFlinger initialization and is expected to remain constant + // thereafter. + static HWCDisplayMetrics display_metrics_; // The hardware composer layer and metrics to use during the prepare cycle. hwc2_layer_t hardware_composer_layer_ = 0; @@ -173,19 +206,21 @@ class Layer { // the previous buffer is returned or an empty value if no buffer has ever // been posted. When a new buffer is acquired the previous buffer's release // fence is passed out automatically. - std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() { + std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t> + Acquire() { if (surface->IsBufferAvailable()) { acquired_buffer.Release(std::move(release_fence)); acquired_buffer = surface->AcquireCurrentBuffer(); ATRACE_ASYNC_END("BufferPost", acquired_buffer.buffer()->id()); } if (!acquired_buffer.IsEmpty()) { - return std::make_tuple(acquired_buffer.buffer()->width(), - acquired_buffer.buffer()->height(), - acquired_buffer.buffer()->buffer()->buffer(), - acquired_buffer.ClaimAcquireFence()); + return std::make_tuple( + acquired_buffer.buffer()->width(), + acquired_buffer.buffer()->height(), acquired_buffer.buffer()->id(), + acquired_buffer.buffer()->buffer()->buffer(), + acquired_buffer.ClaimAcquireFence(), acquired_buffer.slot()); } else { - return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{}); + return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0); } } @@ -217,12 +252,13 @@ class Layer { struct SourceBuffer { std::shared_ptr<IonBuffer> buffer; - std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() { + std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t> + Acquire() { if (buffer) - return std::make_tuple(buffer->width(), buffer->height(), - buffer->buffer(), pdx::LocalHandle{}); + return std::make_tuple(buffer->width(), buffer->height(), -1, + buffer->buffer(), pdx::LocalHandle{}, 0); else - return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{}); + return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0); } void Finish(pdx::LocalHandle /*fence*/) {} @@ -239,6 +275,13 @@ class Layer { pdx::LocalHandle acquire_fence_; bool surface_rect_functions_applied_ = false; + bool pending_visibility_settings_ = true; + + // Map of buffer slot assignments that have already been established with HWC: + // slot -> buffer_id. When this map contains a matching slot and buffer_id the + // buffer argument to setLayerBuffer may be nullptr to avoid the cost of + // importing a buffer HWC already knows about. + std::map<std::size_t, int> cached_buffer_map_; Layer(const Layer&) = delete; void operator=(const Layer&) = delete; @@ -258,16 +301,11 @@ class HardwareComposer { using VSyncCallback = std::function<void(int, int64_t, int64_t, uint32_t)>; using RequestDisplayCallback = std::function<void(bool)>; - // Since there is no universal way to query the number of hardware layers, - // just set it to 4 for now. - static constexpr size_t kMaxHardwareLayers = 4; - HardwareComposer(); - HardwareComposer(Hwc2::Composer* hidl, - RequestDisplayCallback request_display_callback); ~HardwareComposer(); - bool Initialize(); + bool Initialize(Hwc2::Composer* composer, + RequestDisplayCallback request_display_callback); bool IsInitialized() const { return initialized_; } @@ -281,11 +319,6 @@ class HardwareComposer { // Get the HMD display metrics for the current display. display::Metrics GetHmdDisplayMetrics() const; - HWC::Error GetDisplayAttribute(hwc2_display_t display, hwc2_config_t config, - hwc2_attribute_t attributes, - int32_t* out_value) const; - HWC::Error GetDisplayMetrics(hwc2_display_t display, hwc2_config_t config, - HWCDisplayMetrics* out_metrics) const; std::string Dump(); void SetVSyncCallback(VSyncCallback callback); @@ -308,34 +341,37 @@ class HardwareComposer { int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer); void OnDeletedGlobalBuffer(DvrGlobalBufferKey key); - void OnHardwareComposerRefresh(); - private: - int32_t EnableVsync(bool enabled); + HWC::Error GetDisplayAttribute(Hwc2::Composer* composer, + hwc2_display_t display, hwc2_config_t config, + hwc2_attribute_t attributes, + int32_t* out_value) const; + HWC::Error GetDisplayMetrics(Hwc2::Composer* composer, hwc2_display_t display, + hwc2_config_t config, + HWCDisplayMetrics* out_metrics) const; + + HWC::Error EnableVsync(bool enabled); + HWC::Error SetPowerMode(bool active); class ComposerCallback : public Hwc2::IComposerCallback { public: - ComposerCallback() {} - - hardware::Return<void> onHotplug(Hwc2::Display /*display*/, - Connection /*connected*/) override { - // TODO(skiazyk): depending on how the server is implemented, we might - // have to set it up to synchronize with receiving this event, as it can - // potentially be a critical event for setting up state within the - // hwc2 module. That is, we (technically) should not call any other hwc - // methods until this method has been called after registering the - // callbacks. - return hardware::Void(); - } + ComposerCallback() = default; + hardware::Return<void> onHotplug(Hwc2::Display display, + Connection conn) override; + hardware::Return<void> onRefresh(Hwc2::Display display) override; + hardware::Return<void> onVsync(Hwc2::Display display, + int64_t timestamp) override; - hardware::Return<void> onRefresh(Hwc2::Display /*display*/) override { - return hardware::Void(); - } + pdx::Status<int64_t> GetVsyncTime(Hwc2::Display display); - hardware::Return<void> onVsync(Hwc2::Display /*display*/, - int64_t /*timestamp*/) override { - return hardware::Void(); - } + private: + std::mutex vsync_mutex_; + + struct Display { + pdx::LocalHandle driver_vsync_event_fd; + int64_t callback_vsync_timestamp{0}; + }; + std::array<Display, HWC_NUM_PHYSICAL_DISPLAY_TYPES> displays_; }; HWC::Error Validate(hwc2_display_t display); @@ -364,22 +400,21 @@ class HardwareComposer { void UpdatePostThreadState(uint32_t state, bool suspend); // Blocks until either event_fd becomes readable, or we're interrupted by a - // control thread. Any errors are returned as negative errno values. If we're - // interrupted, kPostThreadInterrupted will be returned. + // control thread, or timeout_ms is reached before any events occur. Any + // errors are returned as negative errno values, with -ETIMEDOUT returned in + // the case of a timeout. If we're interrupted, kPostThreadInterrupted will be + // returned. int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd, - int requested_events); + int requested_events, int timeout_ms); - // BlockUntilVSync, WaitForVSync, and SleepUntil are all blocking calls made - // on the post thread that can be interrupted by a control thread. If - // interrupted, these calls return kPostThreadInterrupted. + // WaitForVSync and SleepUntil are blocking calls made on the post thread that + // can be interrupted by a control thread. If interrupted, these calls return + // kPostThreadInterrupted. int ReadWaitPPState(); - int BlockUntilVSync(); - int ReadVSyncTimestamp(int64_t* timestamp); - int WaitForVSync(int64_t* timestamp); + pdx::Status<int64_t> WaitForVSync(); + pdx::Status<int64_t> GetVSyncTime(); int SleepUntil(int64_t wakeup_timestamp); - bool IsFramePendingInDriver() { return ReadWaitPPState() == 1; } - // Reconfigures the layer stack if the display surfaces changed since the last // frame. Called only from the post thread. bool UpdateLayerConfig(); @@ -397,12 +432,11 @@ class HardwareComposer { void UpdateConfigBuffer(); bool initialized_; + bool is_standalone_device_; - // Hardware composer HAL device from SurfaceFlinger. VrFlinger does not own - // this pointer. - Hwc2::Composer* hwc2_hidl_; + std::unique_ptr<Hwc2::Composer> composer_; + sp<ComposerCallback> composer_callback_; RequestDisplayCallback request_display_callback_; - sp<ComposerCallback> callbacks_; // Display metrics of the physical display. HWCDisplayMetrics native_display_metrics_; @@ -417,13 +451,9 @@ class HardwareComposer { // thread and read by the post thread. std::vector<std::shared_ptr<DirectDisplaySurface>> pending_surfaces_; - // The surfaces displayed by the post thread. Used exclusively by the post - // thread. - std::vector<std::shared_ptr<DirectDisplaySurface>> display_surfaces_; - - // Layer array for handling buffer flow into hardware composer layers. - std::array<Layer, kMaxHardwareLayers> layers_; - size_t active_layer_count_ = 0; + // Layer set for handling buffer flow into hardware composer layers. This + // vector must be sorted by surface_id in ascending order. + std::vector<Layer> layers_; // Handler to hook vsync events outside of this class. VSyncCallback vsync_callback_; @@ -433,7 +463,8 @@ class HardwareComposer { std::thread post_thread_; // Post thread state machine and synchronization primitives. - PostThreadStateType post_thread_state_{PostThreadState::Idle}; + PostThreadStateType post_thread_state_{PostThreadState::Idle | + PostThreadState::Suspended}; std::atomic<bool> post_thread_quiescent_{true}; bool post_thread_resumed_{false}; pdx::LocalHandle post_thread_event_fd_; @@ -444,18 +475,15 @@ class HardwareComposer { // Backlight LED brightness sysfs node. pdx::LocalHandle backlight_brightness_fd_; - // Primary display vsync event sysfs node. - pdx::LocalHandle primary_display_vsync_event_fd_; - - // Primary display wait_pingpong state sysfs node. - pdx::LocalHandle primary_display_wait_pp_fd_; - // VSync sleep timerfd. pdx::LocalHandle vsync_sleep_timer_fd_; // The timestamp of the last vsync. int64_t last_vsync_timestamp_ = 0; + // The number of vsync intervals to predict since the last vsync. + int vsync_prediction_interval_ = 1; + // Vsync count since display on. uint32_t vsync_count_ = 0; @@ -478,12 +506,6 @@ class HardwareComposer { static constexpr int kPostThreadInterrupted = 1; - static void HwcRefresh(hwc2_callback_data_t data, hwc2_display_t display); - static void HwcVSync(hwc2_callback_data_t data, hwc2_display_t display, - int64_t timestamp); - static void HwcHotplug(hwc2_callback_data_t callbackData, - hwc2_display_t display, hwc2_connection_t connected); - HardwareComposer(const HardwareComposer&) = delete; void operator=(const HardwareComposer&) = delete; }; diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h index f41da87596..33cbc84d7d 100644 --- a/libs/vr/libvrflinger/include/dvr/vr_flinger.h +++ b/libs/vr/libvrflinger/include/dvr/vr_flinger.h @@ -4,7 +4,7 @@ #include <thread> #include <memory> -#include <pdx/default_transport/service_dispatcher.h> +#include <pdx/service_dispatcher.h> #include <vr/vr_manager/vr_manager.h> namespace android { @@ -29,9 +29,6 @@ class VrFlinger { void GrantDisplayOwnership(); void SeizeDisplayOwnership(); - // Called on a binder thread. - void OnHardwareComposerRefresh(); - // dump all vr flinger state. std::string Dump(); diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp index 3a0ca4a417..85dc586eae 100644 --- a/libs/vr/libvrflinger/vr_flinger.cpp +++ b/libs/vr/libvrflinger/vr_flinger.cpp @@ -18,8 +18,6 @@ #include <sys/prctl.h> #include <sys/resource.h> -#include <pdx/default_transport/service_dispatcher.h> - #include <functional> #include "DisplayHardware/ComposerHal.h" @@ -66,9 +64,6 @@ bool VrFlinger::Init(Hwc2::Composer* hidl, ALOGI("Starting up VrFlinger..."); - setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY); - set_sched_policy(0, SP_FOREGROUND); - // We need to be able to create endpoints with full perms. umask(0000); @@ -76,7 +71,7 @@ bool VrFlinger::Init(Hwc2::Composer* hidl, request_display_callback_ = request_display_callback; - dispatcher_ = android::pdx::default_transport::ServiceDispatcher::Create(); + dispatcher_ = android::pdx::ServiceDispatcher::Create(); CHECK_ERROR(!dispatcher_, error, "Failed to create service dispatcher."); display_service_ = @@ -102,6 +97,9 @@ bool VrFlinger::Init(Hwc2::Composer* hidl, prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0); ALOGI("Entering message loop."); + setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY); + set_sched_policy(0, SP_FOREGROUND); + int ret = dispatcher_->EnterDispatchLoop(); if (ret < 0) { ALOGE("Dispatch loop exited because: %s\n", strerror(-ret)); @@ -135,10 +133,6 @@ void VrFlinger::SeizeDisplayOwnership() { display_service_->SeizeDisplayOwnership(); } -void VrFlinger::OnHardwareComposerRefresh() { - display_service_->OnHardwareComposerRefresh(); -} - std::string VrFlinger::Dump() { // TODO(karthikrs): Add more state information here. return display_service_->DumpState(0/*unused*/); diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp index 3098b43318..fdeb899f66 100644 --- a/libs/vr/libvrflinger/vsync_service.cpp +++ b/libs/vr/libvrflinger/vsync_service.cpp @@ -110,6 +110,7 @@ void VSyncService::UpdateClients() { } pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) { + ATRACE_NAME("VSyncService::HandleMessage"); switch (message.GetOp()) { case VSyncProtocol::Wait::Opcode: AddWaiter(message); diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h index 6a9437c758..b663a67cea 100644 --- a/libs/vr/libvrsensor/include/dvr/pose_client.h +++ b/libs/vr/libvrsensor/include/dvr/pose_client.h @@ -157,6 +157,18 @@ int dvrPoseClientGetRingBuffer(DvrPoseClient* client, // @return Zero on success int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled); +// Requests a burst of data samples from pose service. The data samples are +// passed through a shared memory buffer obtained by calling +// dvrPoseClientGetDataReader(). +// +// @param DvrPoseDataCaptureRequest Parameters on how to capture data. +// @return Zero on success. +int dvrPoseClientDataCapture(DvrPoseClient* client, + const DvrPoseDataCaptureRequest* request); + +// Destroys the write buffer queue for the given |data_type|. +int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h index e4455f1840..7bf1cd4d29 100644 --- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h +++ b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h @@ -17,6 +17,9 @@ enum { DVR_POSE_GET_CONTROLLER_RING_BUFFER, DVR_POSE_LOG_CONTROLLER, DVR_POSE_SENSORS_ENABLE, + DVR_POSE_GET_TANGO_READER, + DVR_POSE_DATA_CAPTURE, + DVR_POSE_TANGO_READER_DESTROY, }; #ifdef __cplusplus diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h new file mode 100644 index 0000000000..39592bb15d --- /dev/null +++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h @@ -0,0 +1,19 @@ +#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ +#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ + +#include <private/dvr/buffer_hub_queue_client.h> + +using android::dvr::ConsumerQueue; + +typedef struct DvrPoseClient DvrPoseClient; + +namespace android { +namespace dvr { + +int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type, + ConsumerQueue **queue_out); + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp index 4ddf1f33cb..4acc085428 100644 --- a/libs/vr/libvrsensor/pose_client.cpp +++ b/libs/vr/libvrsensor/pose_client.cpp @@ -9,23 +9,24 @@ #include <pdx/default_transport/client_channel_factory.h> #include <pdx/file_handle.h> #include <private/dvr/buffer_hub_client.h> +#include <private/dvr/buffer_hub_queue_client.h> #include <private/dvr/display_client.h> #include <private/dvr/pose-ipc.h> #include <private/dvr/shared_buffer_helpers.h> +using android::dvr::ConsumerQueue; using android::pdx::LocalHandle; using android::pdx::LocalChannelHandle; using android::pdx::Status; using android::pdx::Transaction; -#define arraysize(x) (static_cast<int32_t>(std::extent<decltype(x)>::value)) - namespace android { namespace dvr { namespace { typedef CPUMappedBroadcastRing<DvrPoseRing> SensorPoseRing; +constexpr static int32_t MAX_CONTROLLERS = 2; } // namespace // PoseClient is a remote interface to the pose service in sensord. @@ -81,7 +82,7 @@ class PoseClient : public pdx::ClientBase<PoseClient> { int GetControllerPose(int32_t controller_id, uint32_t vsync_count, DvrPoseAsync* out_pose) { - if (controller_id < 0 || controller_id >= arraysize(controllers_)) { + if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) { return -EINVAL; } if (!controllers_[controller_id].mapped_pose_buffer) { @@ -140,6 +141,44 @@ class PoseClient : public pdx::ClientBase<PoseClient> { return ReturnStatusOrError(status); } + int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) { + // Get buffer. + Transaction trans{*this}; + Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>( + DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0); + + if (!status) { + ALOGE("PoseClient GetTangoReaderHandle() failed because: %s", + status.GetErrorMessage().c_str()); + *queue_out = nullptr; + return -status.error(); + } + + std::unique_ptr<ConsumerQueue> consumer_queue = + ConsumerQueue::Import(status.take()); + *queue_out = consumer_queue.release(); + return 0; + } + + int DataCapture(const DvrPoseDataCaptureRequest* request) { + Transaction trans{*this}; + Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request, + sizeof(*request), nullptr, 0); + ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); + } + + int DataReaderDestroy(uint64_t data_type) { + Transaction trans{*this}; + Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY, + &data_type, sizeof(data_type), nullptr, + 0); + ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n", + status.GetErrorMessage().c_str()); + return ReturnStatusOrError(status); + } + // Enables or disables all pose processing from sensors int EnableSensors(bool enabled) { Transaction trans{*this}; @@ -166,7 +205,7 @@ class PoseClient : public pdx::ClientBase<PoseClient> { } int GetControllerRingBuffer(int32_t controller_id) { - if (controller_id < 0 || controller_id >= arraysize(controllers_)) { + if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) { return -EINVAL; } ControllerClientState& client_state = controllers_[controller_id]; @@ -254,9 +293,14 @@ class PoseClient : public pdx::ClientBase<PoseClient> { std::unique_ptr<BufferConsumer> pose_buffer; const DvrPoseAsync* mapped_pose_buffer = nullptr; }; - ControllerClientState controllers_[2]; + ControllerClientState controllers_[MAX_CONTROLLERS]; }; +int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type, + ConsumerQueue** queue_out) { + return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out); +} + } // namespace dvr } // namespace android @@ -308,9 +352,17 @@ int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode) { return PoseClient::FromC(client)->GetMode(mode); } - int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) { return PoseClient::FromC(client)->EnableSensors(enabled); } +int dvrPoseClientDataCapture(DvrPoseClient* client, + const DvrPoseDataCaptureRequest* request) { + return PoseClient::FromC(client)->DataCapture(request); +} + +int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) { + return PoseClient::FromC(client)->DataReaderDestroy(data_type); +} + } // extern "C" diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index c4073a9505..466768ae2c 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -534,11 +534,6 @@ EGLAPI EGLBoolean EGLAPIENTRY eglPresentationTimeANDROID (EGLDisplay dpy, EGLSur #endif #endif /* EGL_ANDROID_presentation_time */ -#ifndef EGL_KHR_no_config_context -#define EGL_KHR_no_config_context 1 -#define EGL_NO_CONFIG_KHR EGL_CAST(EGLConfig,0) -#endif /* EGL_KHR_no_config_context */ - #ifndef EGL_ANDROID_get_frame_timestamps #define EGL_ANDROID_get_frame_timestamps 1 #define EGL_TIMESTAMPS_ANDROID 0x3430 diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp index 62e6bd3055..b67a053021 100644 --- a/opengl/tests/EGLTest/EGL_test.cpp +++ b/opengl/tests/EGLTest/EGL_test.cpp @@ -200,6 +200,7 @@ TEST_F(EGLTest, EGLDisplayP3) { if (!hasWideColorDisplay) { // skip this test if device does not have wide-color display + std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl; return; } @@ -285,6 +286,7 @@ TEST_F(EGLTest, EGLDisplayP31010102) { if (!hasWideColorDisplay) { // skip this test if device does not have wide-color display + std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl; return; } @@ -370,6 +372,7 @@ TEST_F(EGLTest, EGLConfigFP16) { if (!hasWideColorDisplay) { // skip this test if device does not have wide-color display + std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl; return; } @@ -431,9 +434,10 @@ TEST_F(EGLTest, EGLConfigFP16) { EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface)); } -TEST_F(EGLTest, EGL_KHR_no_config_context) { +TEST_F(EGLTest, EGLNoConfigContext) { if (!hasWideColorDisplay) { // skip this test if device does not have wide-color display + std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl; return; } @@ -471,6 +475,7 @@ TEST_F(EGLTest, EGLConfig1010102) { if (!hasWideColorDisplay) { // skip this test if device does not have wide-color display + std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl; return; } diff --git a/opengl/tests/configdump/configdump.cpp b/opengl/tests/configdump/configdump.cpp index c423105a11..a4bb8791ea 100644 --- a/opengl/tests/configdump/configdump.cpp +++ b/opengl/tests/configdump/configdump.cpp @@ -78,7 +78,7 @@ int main(int /*argc*/, char** /*argv*/) { for (EGLint i=0 ; i<n ; i++) { printf("EGLConfig[%d]\n", i); - for (int attr = 0 ; attr<sizeof(attributes)/sizeof(Attribute) ; attr++) { + for (unsigned attr = 0 ; attr<sizeof(attributes)/sizeof(Attribute) ; attr++) { EGLint value; eglGetConfigAttrib(dpy, configs[i], attributes[attr].attribute, &value); printf("\t%-32s: %10d (0x%08x)\n", attributes[attr].name, value, value); diff --git a/services/batteryservice/Android.bp b/services/batteryservice/Android.bp index e441bdafed..4c7265b654 100644 --- a/services/batteryservice/Android.bp +++ b/services/batteryservice/Android.bp @@ -1,3 +1,11 @@ +cc_library_headers { + name: "libbatteryservice_headers", + vendor_available: true, + export_include_dirs: ["include"], + header_libs: ["libbinder_headers"], + export_header_lib_headers: ["libbinder_headers"], +} + cc_library { name: "libbatteryservice", @@ -8,6 +16,9 @@ cc_library { "IBatteryPropertiesRegistrar.cpp", ], + header_libs: ["libbatteryservice_headers"], + export_header_lib_headers: ["libbatteryservice_headers"], + shared_libs: [ "libutils", "libbinder", @@ -19,4 +30,4 @@ cc_library { "-Wunused", "-Wunreachable-code", ], -}
\ No newline at end of file +} diff --git a/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h index 80ab7f3e9e..80ab7f3e9e 100644 --- a/include/batteryservice/BatteryService.h +++ b/services/batteryservice/include/batteryservice/BatteryService.h diff --git a/include/batteryservice/BatteryServiceConstants.h b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h index 8a90a12f2b..8a90a12f2b 100644 --- a/include/batteryservice/BatteryServiceConstants.h +++ b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h diff --git a/include/batteryservice/IBatteryPropertiesListener.h b/services/batteryservice/include/batteryservice/IBatteryPropertiesListener.h index b226dd6fd0..b226dd6fd0 100644 --- a/include/batteryservice/IBatteryPropertiesListener.h +++ b/services/batteryservice/include/batteryservice/IBatteryPropertiesListener.h diff --git a/include/batteryservice/IBatteryPropertiesRegistrar.h b/services/batteryservice/include/batteryservice/IBatteryPropertiesRegistrar.h index a7dbea65e5..a7dbea65e5 100644 --- a/include/batteryservice/IBatteryPropertiesRegistrar.h +++ b/services/batteryservice/include/batteryservice/IBatteryPropertiesRegistrar.h diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index cc81a298a2..d4266f6df9 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -147,7 +147,33 @@ static const int32_t keyCodeRotationMap[][4] = { static const size_t keyCodeRotationMapSize = sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); +static int32_t rotateStemKey(int32_t value, int32_t orientation, + const int32_t map[][2], size_t mapSize) { + if (orientation == DISPLAY_ORIENTATION_180) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][1]; + } + } + } + return value; +} + +// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X +static int32_t stemKeyRotationMap[][2] = { + // key codes enumerated with the original (unrotated) key first + // no rotation, 180 degree rotation + { AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY }, + { AKEYCODE_STEM_1, AKEYCODE_STEM_1 }, + { AKEYCODE_STEM_2, AKEYCODE_STEM_2 }, + { AKEYCODE_STEM_3, AKEYCODE_STEM_3 }, +}; +static const size_t stemKeyRotationMapSize = + sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); + static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { + keyCode = rotateStemKey(keyCode, orientation, + stemKeyRotationMap, stemKeyRotationMapSize); return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, keyCodeRotationMapSize); } @@ -231,7 +257,7 @@ bool InputReaderConfiguration::getDisplayViewport(ViewportType viewportType, const String8* uniqueDisplayId, DisplayViewport* outViewport) const { const DisplayViewport* viewport = NULL; if (viewportType == ViewportType::VIEWPORT_VIRTUAL && uniqueDisplayId != NULL) { - for (DisplayViewport currentViewport : mVirtualDisplays) { + for (const DisplayViewport& currentViewport : mVirtualDisplays) { if (currentViewport.uniqueId == *uniqueDisplayId) { viewport = ¤tViewport; break; @@ -2260,18 +2286,36 @@ void KeyboardInputMapper::configure(nsecs_t when, } } +static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const *property) { + int32_t mapped = 0; + if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { + for (size_t i = 0; i < stemKeyRotationMapSize; i++) { + if (stemKeyRotationMap[i][0] == keyCode) { + stemKeyRotationMap[i][1] = mapped; + return; + } + } + } +} + void KeyboardInputMapper::configureParameters() { mParameters.orientationAware = false; - getDevice()->getConfiguration().tryGetProperty(String8("keyboard.orientationAware"), + const PropertyMap& config = getDevice()->getConfiguration(); + config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); mParameters.hasAssociatedDisplay = false; if (mParameters.orientationAware) { mParameters.hasAssociatedDisplay = true; + + mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); + mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); + mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); + mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); } mParameters.handlesKeyRepeat = false; - getDevice()->getConfiguration().tryGetProperty(String8("keyboard.handlesKeyRepeat"), + config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); } @@ -2926,7 +2970,7 @@ void CursorInputMapper::fadePointer() { // --- RotaryEncoderInputMapper --- RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) : - InputMapper(device) { + InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { mSource = AINPUT_SOURCE_ROTARY_ENCODER; } @@ -2968,6 +3012,14 @@ void RotaryEncoderInputMapper::configure(nsecs_t when, if (!changes) { mRotaryEncoderScrollAccumulator.configure(getDevice()); } + if (!changes || (InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + DisplayViewport v; + if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) { + mOrientation = v.orientation; + } else { + mOrientation = DISPLAY_ORIENTATION_0; + } + } } void RotaryEncoderInputMapper::reset(nsecs_t when) { @@ -3005,6 +3057,10 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { policyFlags |= POLICY_FLAG_WAKE; } + if (mOrientation == DISPLAY_ORIENTATION_180) { + scroll = -scroll; + } + // Send motion event. if (scrolled) { int32_t metaState = mContext->getGlobalMetaState(); diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index c4f786ad0e..a6b9798759 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -1227,6 +1227,7 @@ private: int32_t mSource; float mScalingFactor; + int32_t mOrientation; void sync(nsecs_t when); }; diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp index b54752b08b..3ae7972779 100644 --- a/services/inputflinger/InputWindow.cpp +++ b/services/inputflinger/InputWindow.cpp @@ -49,7 +49,8 @@ bool InputWindowInfo::isTrustedOverlay() const { || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || layoutParamsType == TYPE_DOCK_DIVIDER - || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY; + || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY + || layoutParamsType == TYPE_INPUT_CONSUMER; } bool InputWindowInfo::supportsSplitTouch() const { diff --git a/services/inputflinger/InputWindow.h b/services/inputflinger/InputWindow.h index 610290b2e2..9eb27983cd 100644 --- a/services/inputflinger/InputWindow.h +++ b/services/inputflinger/InputWindow.h @@ -101,6 +101,7 @@ struct InputWindowInfo { TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19, TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20, TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21, + TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22, TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24, TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27, TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32, diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS deleted file mode 100644 index 6a38a1ff14..0000000000 --- a/services/sensorservice/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -ashutoshj@google.com -pengxu@google.com diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index fdb69d3ecc..535d0db2d2 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -217,13 +217,8 @@ ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { } void SensorDevice::autoDisable(void *ident, int handle) { + Info& info( mActivationCount.editValueFor(handle) ); Mutex::Autolock _l(mLock); - ssize_t activationIndex = mActivationCount.indexOfKey(handle); - if (activationIndex < 0) { - ALOGW("Handle %d cannot be found in activation record", handle); - return; - } - Info& info(mActivationCount.editValueAt(activationIndex)); info.removeBatchParamsForIdent(ident); } @@ -234,12 +229,7 @@ status_t SensorDevice::activate(void* ident, int handle, int enabled) { bool actuateHardware = false; Mutex::Autolock _l(mLock); - ssize_t activationIndex = mActivationCount.indexOfKey(handle); - if (activationIndex < 0) { - ALOGW("Handle %d cannot be found in activation record", handle); - return BAD_VALUE; - } - Info& info(mActivationCount.editValueAt(activationIndex)); + Info& info( mActivationCount.editValueFor(handle) ); ALOGD_IF(DEBUG_CONNECTIONS, "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", @@ -333,12 +323,7 @@ status_t SensorDevice::batch( ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); Mutex::Autolock _l(mLock); - ssize_t activationIndex = mActivationCount.indexOfKey(handle); - if (activationIndex < 0) { - ALOGW("Handle %d cannot be found in activation record", handle); - return BAD_VALUE; - } - Info& info(mActivationCount.editValueAt(activationIndex)); + Info& info(mActivationCount.editValueFor(handle)); if (info.batchParams.indexOfKey(ident) < 0) { BatchParams params(samplingPeriodNs, maxBatchReportLatencyNs); diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index cc93105543..4775e4ef54 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -2,3 +2,5 @@ cc_library_static { name: "libsurfaceflingerincludes", export_include_dirs: ["."], } + +subdirs = ["tests/fakehwc"]
\ No newline at end of file diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp index ea048397d0..bef12ea50f 100644 --- a/services/surfaceflinger/DispSync.cpp +++ b/services/surfaceflinger/DispSync.cpp @@ -30,7 +30,7 @@ #include <utils/Trace.h> #include <utils/Vector.h> -#include <ui/Fence.h> +#include <ui/FenceTime.h> #include "DispSync.h" #include "SurfaceFlinger.h" @@ -421,25 +421,13 @@ void DispSync::reset() { resetErrorLocked(); } -bool DispSync::addPresentFence(const sp<Fence>& fence) { +bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { Mutex::Autolock lock(mMutex); - mPresentFences[mPresentSampleOffset] = fence; - mPresentTimes[mPresentSampleOffset] = 0; + mPresentFences[mPresentSampleOffset] = fenceTime; mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES; mNumResyncSamplesSincePresent = 0; - for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - const sp<Fence>& f(mPresentFences[i]); - if (f != NULL) { - nsecs_t t = f->getSignalTime(); - if (t < INT64_MAX) { - mPresentFences[i].clear(); - mPresentTimes[i] = t + mPresentTimeOffset; - } - } - } - updateErrorLocked(); return !mModelUpdated || mError > kErrorThreshold; @@ -604,21 +592,39 @@ void DispSync::updateErrorLocked() { nsecs_t sqErrSum = 0; for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - nsecs_t sample = mPresentTimes[i] - mReferenceTime; - if (sample > mPhase) { - nsecs_t sampleErr = (sample - mPhase) % period; - if (sampleErr > period / 2) { - sampleErr -= period; - } - sqErrSum += sampleErr * sampleErr; - numErrSamples++; + // Only check for the cached value of signal time to avoid unecessary + // syscalls. It is the responsibility of the DispSync owner to + // call getSignalTime() periodically so the cache is updated when the + // fence signals. + nsecs_t time = mPresentFences[i]->getCachedSignalTime(); + if (time == Fence::SIGNAL_TIME_PENDING || + time == Fence::SIGNAL_TIME_INVALID) { + continue; + } + + nsecs_t sample = time - mReferenceTime; + if (sample <= mPhase) { + continue; + } + + nsecs_t sampleErr = (sample - mPhase) % period; + if (sampleErr > period / 2) { + sampleErr -= period; } + sqErrSum += sampleErr * sampleErr; + numErrSamples++; } if (numErrSamples > 0) { mError = sqErrSum / numErrSamples; + mZeroErrSamplesCount = 0; } else { mError = 0; + // Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam. + mZeroErrSamplesCount++; + ALOGE_IF( + (mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == 0, + "No present times for model error."); } if (kTraceDetailedInfo) { @@ -629,9 +635,9 @@ void DispSync::updateErrorLocked() { void DispSync::resetErrorLocked() { mPresentSampleOffset = 0; mError = 0; + mZeroErrSamplesCount = 0; for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - mPresentFences[i].clear(); - mPresentTimes[i] = 0; + mPresentFences[i] = FenceTime::NO_FENCE; } } @@ -670,19 +676,19 @@ void DispSync::dump(String8& result) const { previous = sampleTime; } - result.appendFormat("mPresentFences / mPresentTimes [%d]:\n", + result.appendFormat("mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES); nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - previous = 0; + previous = Fence::SIGNAL_TIME_INVALID; for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES; - bool signaled = mPresentFences[idx] == NULL; - nsecs_t presentTime = mPresentTimes[idx]; - if (!signaled) { + nsecs_t presentTime = mPresentFences[idx]->getSignalTime(); + if (presentTime == Fence::SIGNAL_TIME_PENDING) { result.appendFormat(" [unsignaled fence]\n"); - } else if (presentTime == 0) { - result.appendFormat(" 0\n"); - } else if (previous == 0) { + } else if(presentTime == Fence::SIGNAL_TIME_INVALID) { + result.appendFormat(" [invalid fence]\n"); + } else if (previous == Fence::SIGNAL_TIME_PENDING || + previous == Fence::SIGNAL_TIME_INVALID) { result.appendFormat(" %" PRId64 " (%.3f ms ago)\n", presentTime, (now - presentTime) / 1000000.0); } else { diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h index 3c34fb08a4..880a24d6ad 100644 --- a/services/surfaceflinger/DispSync.h +++ b/services/surfaceflinger/DispSync.h @@ -23,10 +23,14 @@ #include <utils/Timers.h> #include <utils/RefBase.h> +#include <ui/FenceTime.h> + +#include <memory> + namespace android { class String8; -class Fence; +class FenceTime; class DispSyncThread; // DispSync maintains a model of the periodic hardware-based vsync events of a @@ -69,7 +73,7 @@ public: // // This method should be called with the retire fence from each HWComposer // set call that affects the display. - bool addPresentFence(const sp<Fence>& fence); + bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime); // The beginResync, addResyncSample, and endResync methods are used to re- // synchronize the DispSync's model to the hardware vsync events. The re- @@ -131,6 +135,7 @@ private: enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 }; enum { NUM_PRESENT_SAMPLES = 8 }; enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 }; + enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 }; const char* const mName; @@ -148,9 +153,14 @@ private: // mError is the computed model error. It is based on the difference // between the estimated vsync event times and those observed in the - // mPresentTimes array. + // mPresentFences array. nsecs_t mError; + // mZeroErrSamplesCount keeps track of how many times in a row there were + // zero timestamps available in the mPresentFences array. + // Used to sanity check that we are able to calculate the model error. + size_t mZeroErrSamplesCount; + // Whether we have updated the vsync event model since the last resync. bool mModelUpdated; @@ -164,8 +174,8 @@ private: // These member variables store information about the present fences used // to validate the currently computed model. - sp<Fence> mPresentFences[NUM_PRESENT_SAMPLES]; - nsecs_t mPresentTimes[NUM_PRESENT_SAMPLES]; + std::shared_ptr<FenceTime> + mPresentFences[NUM_PRESENT_SAMPLES] {FenceTime::NO_FENCE}; size_t mPresentSampleOffset; int mRefreshSkipCount; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index fc60002334..248ef53f55 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -65,7 +65,7 @@ using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; static bool useTripleFramebuffer = getInt64< ISurfaceFlingerConfigs, - &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) == 3; + &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) >= 3; #if !defined(EGL_EGLEXT_PROTOTYPES) || !defined(EGL_ANDROID_swap_rectangle) // Dummy implementation in case it is missing. @@ -134,9 +134,11 @@ DisplayDevice::DisplayDevice( EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (config == EGL_NO_CONFIG) { #ifdef USE_HWC2 - config = RenderEngine::chooseEglConfig(display, PIXEL_FORMAT_RGBA_8888); + config = RenderEngine::chooseEglConfig(display, PIXEL_FORMAT_RGBA_8888, + /*logConfig*/ false); #else - config = RenderEngine::chooseEglConfig(display, format); + config = RenderEngine::chooseEglConfig(display, format, + /*logConfig*/ false); #endif } eglSurface = eglCreateWindowSurface(display, config, window, NULL); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index e34fa163c4..7d6d9886f6 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -157,15 +157,11 @@ void Composer::CommandWriter::writeBufferMetadata( write64(metadata.usage); } -Composer::Composer(bool useVrComposer) +Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize), - mIsUsingVrComposer(useVrComposer) + mIsUsingVrComposer(serviceName == std::string("vr")) { - if (mIsUsingVrComposer) { - mComposer = IComposer::getService("vr"); - } else { - mComposer = IComposer::getService(); // use default name - } + mComposer = IComposer::getService(serviceName); if (mComposer == nullptr) { LOG_ALWAYS_FATAL("failed to get hwcomposer service"); @@ -219,6 +215,10 @@ void Composer::registerCallback(const sp<IComposerCallback>& callback) } } +bool Composer::isRemote() { + return mClient->isRemote(); +} + void Composer::resetCommands() { mWriter.reset(); } @@ -751,7 +751,7 @@ Error Composer::execute() } Error error = kDefaultError; - mClient->executeCommands(commandLength, commandHandles, + auto ret = mClient->executeCommands(commandLength, commandHandles, [&](const auto& tmpError, const auto& tmpOutChanged, const auto& tmpOutLength, const auto& tmpOutHandles) { @@ -784,6 +784,11 @@ Error Composer::execute() error = Error::NO_RESOURCES; } }); + // executeCommands can fail because of out-of-fd and we do not want to + // abort() in that case + if (!ret.isOk()) { + ALOGE("executeCommands failed because of %s", ret.description().c_str()); + } if (error == Error::NONE) { std::vector<CommandReader::CommandError> commandErrors = diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 96dd833cd5..31a3c1d785 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -136,13 +136,18 @@ private: // Composer is a wrapper to IComposer, a proxy to server-side composer. class Composer { public: - Composer(bool useVrComposer); + Composer(const std::string& serviceName); std::vector<IComposer::Capability> getCapabilities(); std::string dumpDebugInfo(); void registerCallback(const sp<IComposerCallback>& callback); + // Returns true if the connected composer service is running in a remote + // process, false otherwise. This will return false if the service is + // configured in passthrough mode, for example. + bool isRemote(); + // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. void resetCommands(); diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 1ac21c6210..93c6d5486f 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -34,6 +34,7 @@ #include <gui/BufferQueue.h> #include <gui/Surface.h> +#include <ui/DebugUtils.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> @@ -103,6 +104,7 @@ status_t FramebufferSurface::advanceFrame() { sp<Fence> acquireFence(Fence::NO_FENCE); android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN; status_t result = nextBuffer(slot, buf, acquireFence, dataspace); + mDataSpace = dataspace; if (result != NO_ERROR) { ALOGE("error latching next FramebufferSurface buffer: %s (%d)", strerror(-result), result); @@ -249,7 +251,10 @@ status_t FramebufferSurface::compositionComplete() #endif void FramebufferSurface::dumpAsString(String8& result) const { - ConsumerBase::dumpState(result); + Mutex::Autolock lock(mMutex); + result.appendFormat("FramebufferSurface: dataspace: %s(%d)\n", + dataspaceDetails(mDataSpace).c_str(), mDataSpace); + ConsumerBase::dumpLocked(result, ""); } void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index 69a72d7ede..a1756ca3c2 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -83,6 +83,13 @@ private: // or the buffer is not associated with a slot. int mCurrentBufferSlot; + // mDataSpace is the dataspace of the current composition buffer for + // this FramebufferSurface. It will be 0 when HWC is doing the + // compositing. Otherwise it will display the dataspace of the buffer + // use for compositing which can change as wide-color content is + // on/off. + android_dataspace mDataSpace; + // mCurrentBuffer is the current buffer or NULL to indicate that there is // no current buffer. sp<GraphicBuffer> mCurrentBuffer; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 270a73228b..78c0c8567a 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -33,45 +33,6 @@ #include <algorithm> #include <inttypes.h> -extern "C" { - static void hotplug_hook(hwc2_callback_data_t callbackData, - hwc2_display_t displayId, int32_t intConnected) { - auto device = static_cast<HWC2::Device*>(callbackData); - auto display = device->getDisplayById(displayId); - if (display) { - auto connected = static_cast<HWC2::Connection>(intConnected); - device->callHotplug(std::move(display), connected); - } else { - ALOGE("Hotplug callback called with unknown display %" PRIu64, - displayId); - } - } - - static void refresh_hook(hwc2_callback_data_t callbackData, - hwc2_display_t displayId) { - auto device = static_cast<HWC2::Device*>(callbackData); - auto display = device->getDisplayById(displayId); - if (display) { - device->callRefresh(std::move(display)); - } else { - ALOGE("Refresh callback called with unknown display %" PRIu64, - displayId); - } - } - - static void vsync_hook(hwc2_callback_data_t callbackData, - hwc2_display_t displayId, int64_t timestamp) { - auto device = static_cast<HWC2::Device*>(callbackData); - auto display = device->getDisplayById(displayId); - if (display) { - device->callVsync(std::move(display), timestamp); - } else { - ALOGE("Vsync callback called with unknown display %" PRIu64, - displayId); - } - } -} - using android::Fence; using android::FloatRect; using android::GraphicBuffer; @@ -86,51 +47,78 @@ namespace HWC2 { namespace Hwc2 = android::Hwc2; +namespace { + +class ComposerCallbackBridge : public Hwc2::IComposerCallback { +public: + ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId) + : mCallback(callback), mSequenceId(sequenceId), + mHasPrimaryDisplay(false) {} + + Return<void> onHotplug(Hwc2::Display display, + IComposerCallback::Connection conn) override + { + HWC2::Connection connection = static_cast<HWC2::Connection>(conn); + if (!mHasPrimaryDisplay) { + LOG_ALWAYS_FATAL_IF(connection != HWC2::Connection::Connected, + "Initial onHotplug callback should be " + "primary display connected"); + mHasPrimaryDisplay = true; + mCallback->onHotplugReceived(mSequenceId, display, + connection, true); + } else { + mCallback->onHotplugReceived(mSequenceId, display, + connection, false); + } + return Void(); + } + + Return<void> onRefresh(Hwc2::Display display) override + { + mCallback->onRefreshReceived(mSequenceId, display); + return Void(); + } + + Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override + { + mCallback->onVsyncReceived(mSequenceId, display, timestamp); + return Void(); + } + + bool HasPrimaryDisplay() { return mHasPrimaryDisplay; } + +private: + ComposerCallback* mCallback; + int32_t mSequenceId; + bool mHasPrimaryDisplay; +}; + +} // namespace anonymous + + // Device methods -Device::Device(bool useVrComposer) - : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)), +Device::Device(const std::string& serviceName) + : mComposer(std::make_unique<Hwc2::Composer>(serviceName)), mCapabilities(), mDisplays(), - mHotplug(), - mPendingHotplugs(), - mRefresh(), - mPendingRefreshes(), - mVsync(), - mPendingVsyncs() + mRegisteredCallback(false) { loadCapabilities(); - registerCallbacks(); } -Device::~Device() -{ - for (auto element : mDisplays) { - auto display = element.second.lock(); - if (!display) { - ALOGE("~Device: Found a display (%" PRId64 " that has already been" - " destroyed", element.first); - continue; - } - - DisplayType displayType = HWC2::DisplayType::Invalid; - auto error = display->getType(&displayType); - if (error != Error::None) { - ALOGE("~Device: Failed to determine type of display %" PRIu64 - ": %s (%d)", display->getId(), to_string(error).c_str(), - static_cast<int32_t>(error)); - continue; - } - - if (displayType == HWC2::DisplayType::Physical) { - error = display->setVsyncEnabled(HWC2::Vsync::Disable); - if (error != Error::None) { - ALOGE("~Device: Failed to disable vsync for display %" PRIu64 - ": %s (%d)", display->getId(), to_string(error).c_str(), - static_cast<int32_t>(error)); - } - } +void Device::registerCallback(ComposerCallback* callback, int32_t sequenceId) { + if (mRegisteredCallback) { + ALOGW("Callback already registered. Ignored extra registration " + "attempt."); + return; } + mRegisteredCallback = true; + sp<ComposerCallbackBridge> callbackBridge( + new ComposerCallbackBridge(callback, sequenceId)); + mComposer->registerCallback(callbackBridge); + LOG_ALWAYS_FATAL_IF(!callbackBridge->HasPrimaryDisplay(), + "Registered composer callback but didn't get primary display"); } // Required by HWC2 device @@ -146,7 +134,7 @@ uint32_t Device::getMaxVirtualDisplayCount() const } Error Device::createVirtualDisplay(uint32_t width, uint32_t height, - android_pixel_format_t* format, std::shared_ptr<Display>* outDisplay) + android_pixel_format_t* format, Display** outDisplay) { ALOGI("Creating virtual display"); @@ -159,104 +147,66 @@ Error Device::createVirtualDisplay(uint32_t width, uint32_t height, return error; } - ALOGI("Created virtual display"); + auto display = std::make_unique<Display>( + *mComposer.get(), mCapabilities, displayId, DisplayType::Virtual); + *outDisplay = display.get(); *format = static_cast<android_pixel_format_t>(intFormat); - *outDisplay = getDisplayById(displayId); - if (!*outDisplay) { - ALOGE("Failed to get display by id"); - return Error::BadDisplay; - } - (*outDisplay)->setConnected(true); + mDisplays.emplace(displayId, std::move(display)); + ALOGI("Created virtual display"); return Error::None; } -void Device::registerHotplugCallback(HotplugCallback hotplug) +void Device::destroyDisplay(hwc2_display_t displayId) { - ALOGV("registerHotplugCallback"); - mHotplug = hotplug; - for (auto& pending : mPendingHotplugs) { - auto& display = pending.first; - auto connected = pending.second; - ALOGV("Sending pending hotplug(%" PRIu64 ", %s)", display->getId(), - to_string(connected).c_str()); - mHotplug(std::move(display), connected); - } + ALOGI("Destroying display %" PRIu64, displayId); + mDisplays.erase(displayId); } -void Device::registerRefreshCallback(RefreshCallback refresh) -{ - mRefresh = refresh; - for (auto& pending : mPendingRefreshes) { - mRefresh(std::move(pending)); - } -} - -void Device::registerVsyncCallback(VsyncCallback vsync) -{ - mVsync = vsync; - for (auto& pending : mPendingVsyncs) { - auto& display = pending.first; - auto timestamp = pending.second; - mVsync(std::move(display), timestamp); - } -} - -// For use by Device callbacks +void Device::onHotplug(hwc2_display_t displayId, Connection connection) { + if (connection == Connection::Connected) { + auto display = getDisplayById(displayId); + if (display) { + if (display->isConnected()) { + ALOGW("Attempt to hotplug connect display %" PRIu64 + " , which is already connected.", displayId); + } else { + display->setConnected(true); + } + } else { + DisplayType displayType; + auto intError = mComposer->getDisplayType(displayId, + reinterpret_cast<Hwc2::IComposerClient::DisplayType *>( + &displayType)); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d). " + "Aborting hotplug attempt.", + displayId, to_string(error).c_str(), intError); + return; + } -void Device::callHotplug(std::shared_ptr<Display> display, Connection connected) -{ - if (connected == Connection::Connected) { - if (!display->isConnected()) { - mComposer->setClientTargetSlotCount(display->getId()); - display->loadConfigs(); - display->setConnected(true); + auto newDisplay = std::make_unique<Display>( + *mComposer.get(), mCapabilities, displayId, displayType); + mDisplays.emplace(displayId, std::move(newDisplay)); + } + } else if (connection == Connection::Disconnected) { + // The display will later be destroyed by a call to + // destroyDisplay(). For now we just mark it disconnected. + auto display = getDisplayById(displayId); + if (display) { + display->setConnected(false); + } else { + ALOGW("Attempted to disconnect unknown display %" PRIu64, + displayId); } - } else { - display->setConnected(false); - mDisplays.erase(display->getId()); - } - - if (mHotplug) { - mHotplug(std::move(display), connected); - } else { - ALOGV("callHotplug called, but no valid callback registered, storing"); - mPendingHotplugs.emplace_back(std::move(display), connected); - } -} - -void Device::callRefresh(std::shared_ptr<Display> display) -{ - if (mRefresh) { - mRefresh(std::move(display)); - } else { - ALOGV("callRefresh called, but no valid callback registered, storing"); - mPendingRefreshes.emplace_back(std::move(display)); - } -} - -void Device::callVsync(std::shared_ptr<Display> display, nsecs_t timestamp) -{ - if (mVsync) { - mVsync(std::move(display), timestamp); - } else { - ALOGV("callVsync called, but no valid callback registered, storing"); - mPendingVsyncs.emplace_back(std::move(display), timestamp); } } // Other Device methods -std::shared_ptr<Display> Device::getDisplayById(hwc2_display_t id) { - if (mDisplays.count(id) != 0) { - auto strongDisplay = mDisplays[id].lock(); - ALOGE_IF(!strongDisplay, "Display %" PRId64 " is in mDisplays but is no" - " longer alive", id); - return strongDisplay; - } - - auto display = std::make_shared<Display>(*this, id); - mDisplays.emplace(id, display); - return display; +Display* Device::getDisplayById(hwc2_display_t id) { + auto iter = mDisplays.find(id); + return iter == mDisplays.end() ? nullptr : iter->second.get(); } // Device initialization methods @@ -271,84 +221,37 @@ void Device::loadCapabilities() } } -bool Device::hasCapability(HWC2::Capability capability) const -{ - return std::find(mCapabilities.cbegin(), mCapabilities.cend(), - capability) != mCapabilities.cend(); -} - -namespace { -class ComposerCallback : public Hwc2::IComposerCallback { -public: - ComposerCallback(Device* device) : mDevice(device) {} - - Return<void> onHotplug(Hwc2::Display display, - Connection connected) override - { - hotplug_hook(mDevice, display, static_cast<int32_t>(connected)); - return Void(); - } - - Return<void> onRefresh(Hwc2::Display display) override - { - refresh_hook(mDevice, display); - return Void(); - } - - Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override - { - vsync_hook(mDevice, display, timestamp); - return Void(); - } - -private: - Device* mDevice; -}; -} // namespace anonymous - -void Device::registerCallbacks() -{ - sp<ComposerCallback> callback = new ComposerCallback(this); - mComposer->registerCallback(callback); -} - - -// For use by Display - -void Device::destroyVirtualDisplay(hwc2_display_t display) -{ - ALOGI("Destroying virtual display"); - auto intError = mComposer->destroyVirtualDisplay(display); - auto error = static_cast<Error>(intError); - ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:" - " %s (%d)", display, to_string(error).c_str(), intError); - mDisplays.erase(display); -} - // Display methods -Display::Display(Device& device, hwc2_display_t id) - : mDevice(device), +Display::Display(android::Hwc2::Composer& composer, + const std::unordered_set<Capability>& capabilities, + hwc2_display_t id, DisplayType type) + : mComposer(composer), + mCapabilities(capabilities), mId(id), mIsConnected(false), - mType(DisplayType::Invalid) + mType(type) { ALOGV("Created display %" PRIu64, id); - - auto intError = mDevice.mComposer->getDisplayType(mId, - reinterpret_cast<Hwc2::IComposerClient::DisplayType *>(&mType)); - auto error = static_cast<Error>(intError); - if (error != Error::None) { - ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d)", - id, to_string(error).c_str(), intError); - } + setConnected(true); } -Display::~Display() -{ - ALOGV("Destroyed display %" PRIu64, mId); +Display::~Display() { + mLayers.clear(); + if (mType == DisplayType::Virtual) { - mDevice.destroyVirtualDisplay(mId); + ALOGV("Destroying virtual display"); + auto intError = mComposer.destroyVirtualDisplay(mId); + auto error = static_cast<Error>(intError); + ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 + ") failed: %s (%d)", mId, to_string(error).c_str(), intError); + } else if (mType == DisplayType::Physical) { + auto error = setVsyncEnabled(HWC2::Vsync::Disable); + if (error != Error::None) { + ALOGE("~Display: Failed to disable vsync for display %" PRIu64 + ": %s (%d)", mId, to_string(error).c_str(), + static_cast<int32_t>(error)); + } } } @@ -383,22 +286,35 @@ float Display::Config::Builder::getDefaultDensity() { Error Display::acceptChanges() { - auto intError = mDevice.mComposer->acceptDisplayChanges(mId); + auto intError = mComposer.acceptDisplayChanges(mId); return static_cast<Error>(intError); } -Error Display::createLayer(std::shared_ptr<Layer>* outLayer) +Error Display::createLayer(Layer** outLayer) { + if (!outLayer) { + return Error::BadParameter; + } hwc2_layer_t layerId = 0; - auto intError = mDevice.mComposer->createLayer(mId, &layerId); + auto intError = mComposer.createLayer(mId, &layerId); auto error = static_cast<Error>(intError); if (error != Error::None) { return error; } - auto layer = std::make_shared<Layer>(shared_from_this(), layerId); - mLayers.emplace(layerId, layer); - *outLayer = std::move(layer); + auto layer = std::make_unique<Layer>( + mComposer, mCapabilities, mId, layerId); + *outLayer = layer.get(); + mLayers.emplace(layerId, std::move(layer)); + return Error::None; +} + +Error Display::destroyLayer(Layer* layer) +{ + if (!layer) { + return Error::BadParameter; + } + mLayers.erase(layer->getId()); return Error::None; } @@ -407,7 +323,7 @@ Error Display::getActiveConfig( { ALOGV("[%" PRIu64 "] getActiveConfig", mId); hwc2_config_t configId = 0; - auto intError = mDevice.mComposer->getActiveConfig(mId, &configId); + auto intError = mComposer.getActiveConfig(mId, &configId); auto error = static_cast<Error>(intError); if (error != Error::None) { @@ -430,12 +346,12 @@ Error Display::getActiveConfig( } Error Display::getChangedCompositionTypes( - std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes) + std::unordered_map<Layer*, Composition>* outTypes) { std::vector<Hwc2::Layer> layerIds; std::vector<Hwc2::IComposerClient::Composition> types; - auto intError = mDevice.mComposer->getChangedCompositionTypes(mId, - &layerIds, &types); + auto intError = mComposer.getChangedCompositionTypes( + mId, &layerIds, &types); uint32_t numElements = layerIds.size(); auto error = static_cast<Error>(intError); error = static_cast<Error>(intError); @@ -464,7 +380,7 @@ Error Display::getChangedCompositionTypes( Error Display::getColorModes(std::vector<android_color_mode_t>* outModes) const { std::vector<Hwc2::ColorMode> modes; - auto intError = mDevice.mComposer->getColorModes(mId, &modes); + auto intError = mComposer.getColorModes(mId, &modes); uint32_t numModes = modes.size(); auto error = static_cast<Error>(intError); if (error != Error::None) { @@ -489,19 +405,18 @@ std::vector<std::shared_ptr<const Display::Config>> Display::getConfigs() const Error Display::getName(std::string* outName) const { - auto intError = mDevice.mComposer->getDisplayName(mId, outName); + auto intError = mComposer.getDisplayName(mId, outName); return static_cast<Error>(intError); } Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests, - std::unordered_map<std::shared_ptr<Layer>, LayerRequest>* - outLayerRequests) + std::unordered_map<Layer*, LayerRequest>* outLayerRequests) { uint32_t intDisplayRequests; std::vector<Hwc2::Layer> layerIds; std::vector<uint32_t> layerRequests; - auto intError = mDevice.mComposer->getDisplayRequests(mId, - &intDisplayRequests, &layerIds, &layerRequests); + auto intError = mComposer.getDisplayRequests( + mId, &intDisplayRequests, &layerIds, &layerRequests); uint32_t numElements = layerIds.size(); auto error = static_cast<Error>(intError); if (error != Error::None) { @@ -535,7 +450,7 @@ Error Display::getType(DisplayType* outType) const Error Display::supportsDoze(bool* outSupport) const { bool intSupport = false; - auto intError = mDevice.mComposer->getDozeSupport(mId, &intSupport); + auto intError = mComposer.getDozeSupport(mId, &intSupport); auto error = static_cast<Error>(intError); if (error != Error::None) { return error; @@ -552,7 +467,7 @@ Error Display::getHdrCapabilities( float maxAverageLuminance = -1.0f; float minLuminance = -1.0f; std::vector<Hwc2::Hdr> intTypes; - auto intError = mDevice.mComposer->getHdrCapabilities(mId, &intTypes, + auto intError = mComposer.getHdrCapabilities(mId, &intTypes, &maxLuminance, &maxAverageLuminance, &minLuminance); auto error = static_cast<HWC2::Error>(intError); @@ -571,25 +486,24 @@ Error Display::getHdrCapabilities( } Error Display::getReleaseFences( - std::unordered_map<std::shared_ptr<Layer>, sp<Fence>>* outFences) const + std::unordered_map<Layer*, sp<Fence>>* outFences) const { std::vector<Hwc2::Layer> layerIds; std::vector<int> fenceFds; - auto intError = mDevice.mComposer->getReleaseFences(mId, - &layerIds, &fenceFds); + auto intError = mComposer.getReleaseFences(mId, &layerIds, &fenceFds); auto error = static_cast<Error>(intError); uint32_t numElements = layerIds.size(); if (error != Error::None) { return error; } - std::unordered_map<std::shared_ptr<Layer>, sp<Fence>> releaseFences; + std::unordered_map<Layer*, sp<Fence>> releaseFences; releaseFences.reserve(numElements); for (uint32_t element = 0; element < numElements; ++element) { auto layer = getLayerById(layerIds[element]); if (layer) { sp<Fence> fence(new Fence(fenceFds[element])); - releaseFences.emplace(std::move(layer), fence); + releaseFences.emplace(layer, fence); } else { ALOGE("getReleaseFences: invalid layer %" PRIu64 " found on display %" PRIu64, layerIds[element], mId); @@ -607,7 +521,7 @@ Error Display::getReleaseFences( Error Display::present(sp<Fence>* outPresentFence) { int32_t presentFenceFd = -1; - auto intError = mDevice.mComposer->presentDisplay(mId, &presentFenceFd); + auto intError = mComposer.presentDisplay(mId, &presentFenceFd); auto error = static_cast<Error>(intError); if (error != Error::None) { return error; @@ -625,7 +539,7 @@ Error Display::setActiveConfig(const std::shared_ptr<const Config>& config) config->getDisplayId(), mId); return Error::BadConfig; } - auto intError = mDevice.mComposer->setActiveConfig(mId, config->getId()); + auto intError = mComposer.setActiveConfig(mId, config->getId()); return static_cast<Error>(intError); } @@ -634,7 +548,7 @@ Error Display::setClientTarget(uint32_t slot, const sp<GraphicBuffer>& target, { // TODO: Properly encode client target surface damage int32_t fenceFd = acquireFence->dup(); - auto intError = mDevice.mComposer->setClientTarget(mId, slot, target, + auto intError = mComposer.setClientTarget(mId, slot, target, fenceFd, static_cast<Hwc2::Dataspace>(dataspace), std::vector<Hwc2::IComposerClient::Rect>()); return static_cast<Error>(intError); @@ -642,15 +556,15 @@ Error Display::setClientTarget(uint32_t slot, const sp<GraphicBuffer>& target, Error Display::setColorMode(android_color_mode_t mode) { - auto intError = mDevice.mComposer->setColorMode(mId, - static_cast<Hwc2::ColorMode>(mode)); + auto intError = mComposer.setColorMode( + mId, static_cast<Hwc2::ColorMode>(mode)); return static_cast<Error>(intError); } Error Display::setColorTransform(const android::mat4& matrix, android_color_transform_t hint) { - auto intError = mDevice.mComposer->setColorTransform(mId, + auto intError = mComposer.setColorTransform(mId, matrix.asArray(), static_cast<Hwc2::ColorTransform>(hint)); return static_cast<Error>(intError); } @@ -660,7 +574,7 @@ Error Display::setOutputBuffer(const sp<GraphicBuffer>& buffer, { int32_t fenceFd = releaseFence->dup(); auto handle = buffer->getNativeBuffer()->handle; - auto intError = mDevice.mComposer->setOutputBuffer(mId, handle, fenceFd); + auto intError = mComposer.setOutputBuffer(mId, handle, fenceFd); close(fenceFd); return static_cast<Error>(intError); } @@ -668,14 +582,14 @@ Error Display::setOutputBuffer(const sp<GraphicBuffer>& buffer, Error Display::setPowerMode(PowerMode mode) { auto intMode = static_cast<Hwc2::IComposerClient::PowerMode>(mode); - auto intError = mDevice.mComposer->setPowerMode(mId, intMode); + auto intError = mComposer.setPowerMode(mId, intMode); return static_cast<Error>(intError); } Error Display::setVsyncEnabled(Vsync enabled) { auto intEnabled = static_cast<Hwc2::IComposerClient::Vsync>(enabled); - auto intError = mDevice.mComposer->setVsyncEnabled(mId, intEnabled); + auto intError = mComposer.setVsyncEnabled(mId, intEnabled); return static_cast<Error>(intError); } @@ -683,8 +597,7 @@ Error Display::validate(uint32_t* outNumTypes, uint32_t* outNumRequests) { uint32_t numTypes = 0; uint32_t numRequests = 0; - auto intError = mDevice.mComposer->validateDisplay(mId, - &numTypes, &numRequests); + auto intError = mComposer.validateDisplay(mId, &numTypes, &numRequests); auto error = static_cast<Error>(intError); if (error != Error::None && error != Error::HasChanges) { return error; @@ -701,7 +614,8 @@ Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests uint32_t numTypes = 0; uint32_t numRequests = 0; int32_t presentFenceFd = -1; - auto intError = mDevice.mComposer->presentOrValidateDisplay(mId, &numTypes, &numRequests, &presentFenceFd, state); + auto intError = mComposer.presentOrValidateDisplay( + mId, &numTypes, &numRequests, &presentFenceFd, state); auto error = static_cast<Error>(intError); if (error != Error::None && error != Error::HasChanges) { return error; @@ -720,15 +634,23 @@ Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests void Display::discardCommands() { - mDevice.mComposer->resetCommands(); + mComposer.resetCommands(); } // For use by Device +void Display::setConnected(bool connected) { + if (!mIsConnected && connected && mType == DisplayType::Physical) { + mComposer.setClientTargetSlotCount(mId); + loadConfigs(); + } + mIsConnected = connected; +} + int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute) { int32_t value = 0; - auto intError = mDevice.mComposer->getDisplayAttribute(mId, configId, + auto intError = mComposer.getDisplayAttribute(mId, configId, static_cast<Hwc2::IComposerClient::Attribute>(attribute), &value); auto error = static_cast<Error>(intError); @@ -760,7 +682,7 @@ void Display::loadConfigs() ALOGV("[%" PRIu64 "] loadConfigs", mId); std::vector<Hwc2::Config> configIds; - auto intError = mDevice.mComposer->getDisplayConfigs(mId, &configIds); + auto intError = mComposer.getDisplayConfigs(mId, &configIds); auto error = static_cast<Error>(intError); if (error != Error::None) { ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId, @@ -773,54 +695,51 @@ void Display::loadConfigs() } } -// For use by Layer - -void Display::destroyLayer(hwc2_layer_t layerId) -{ - auto intError =mDevice.mComposer->destroyLayer(mId, layerId); - auto error = static_cast<Error>(intError); - ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")" - " failed: %s (%d)", mId, layerId, to_string(error).c_str(), - intError); - mLayers.erase(layerId); -} - // Other Display methods -std::shared_ptr<Layer> Display::getLayerById(hwc2_layer_t id) const +Layer* Display::getLayerById(hwc2_layer_t id) const { if (mLayers.count(id) == 0) { return nullptr; } - auto layer = mLayers.at(id).lock(); - return layer; + return mLayers.at(id).get(); } // Layer methods -Layer::Layer(const std::shared_ptr<Display>& display, hwc2_layer_t id) - : mDisplay(display), - mDisplayId(display->getId()), - mDevice(display->getDevice()), - mId(id) +Layer::Layer(android::Hwc2::Composer& composer, + const std::unordered_set<Capability>& capabilities, + hwc2_display_t displayId, hwc2_layer_t layerId) + : mComposer(composer), + mCapabilities(capabilities), + mDisplayId(displayId), + mId(layerId) { - ALOGV("Created layer %" PRIu64 " on display %" PRIu64, id, - display->getId()); + ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId); } Layer::~Layer() { - auto display = mDisplay.lock(); - if (display) { - display->destroyLayer(mId); + auto intError = mComposer.destroyLayer(mDisplayId, mId); + auto error = static_cast<Error>(intError); + ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")" + " failed: %s (%d)", mDisplayId, mId, to_string(error).c_str(), + intError); + if (mLayerDestroyedListener) { + mLayerDestroyedListener(this); } } +void Layer::setLayerDestroyedListener(std::function<void(Layer*)> listener) { + LOG_ALWAYS_FATAL_IF(mLayerDestroyedListener && listener, + "Attempt to set layer destroyed listener multiple times"); + mLayerDestroyedListener = listener; +} + Error Layer::setCursorPosition(int32_t x, int32_t y) { - auto intError = mDevice.mComposer->setCursorPosition(mDisplayId, - mId, x, y); + auto intError = mComposer.setCursorPosition(mDisplayId, mId, x, y); return static_cast<Error>(intError); } @@ -828,8 +747,8 @@ Error Layer::setBuffer(uint32_t slot, const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence) { int32_t fenceFd = acquireFence->dup(); - auto intError = mDevice.mComposer->setLayerBuffer(mDisplayId, - mId, slot, buffer, fenceFd); + auto intError = mComposer.setLayerBuffer(mDisplayId, mId, slot, buffer, + fenceFd); return static_cast<Error>(intError); } @@ -839,7 +758,7 @@ Error Layer::setSurfaceDamage(const Region& damage) // rects for HWC Hwc2::Error intError = Hwc2::Error::NONE; if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) { - intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId, + intError = mComposer.setLayerSurfaceDamage(mDisplayId, mId, std::vector<Hwc2::IComposerClient::Rect>()); } else { size_t rectCount = 0; @@ -851,8 +770,7 @@ Error Layer::setSurfaceDamage(const Region& damage) rectArray[rect].right, rectArray[rect].bottom}); } - intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId, - mId, hwcRects); + intError = mComposer.setLayerSurfaceDamage(mDisplayId, mId, hwcRects); } return static_cast<Error>(intError); @@ -861,24 +779,22 @@ Error Layer::setSurfaceDamage(const Region& damage) Error Layer::setBlendMode(BlendMode mode) { auto intMode = static_cast<Hwc2::IComposerClient::BlendMode>(mode); - auto intError = mDevice.mComposer->setLayerBlendMode(mDisplayId, - mId, intMode); + auto intError = mComposer.setLayerBlendMode(mDisplayId, mId, intMode); return static_cast<Error>(intError); } Error Layer::setColor(hwc_color_t color) { Hwc2::IComposerClient::Color hwcColor{color.r, color.g, color.b, color.a}; - auto intError = mDevice.mComposer->setLayerColor(mDisplayId, - mId, hwcColor); + auto intError = mComposer.setLayerColor(mDisplayId, mId, hwcColor); return static_cast<Error>(intError); } Error Layer::setCompositionType(Composition type) { auto intType = static_cast<Hwc2::IComposerClient::Composition>(type); - auto intError = mDevice.mComposer->setLayerCompositionType(mDisplayId, - mId, intType); + auto intError = mComposer.setLayerCompositionType( + mDisplayId, mId, intType); return static_cast<Error>(intError); } @@ -889,8 +805,7 @@ Error Layer::setDataspace(android_dataspace_t dataspace) } mDataSpace = dataspace; auto intDataspace = static_cast<Hwc2::Dataspace>(dataspace); - auto intError = mDevice.mComposer->setLayerDataspace(mDisplayId, - mId, intDataspace); + auto intError = mComposer.setLayerDataspace(mDisplayId, mId, intDataspace); return static_cast<Error>(intError); } @@ -898,27 +813,24 @@ Error Layer::setDisplayFrame(const Rect& frame) { Hwc2::IComposerClient::Rect hwcRect{frame.left, frame.top, frame.right, frame.bottom}; - auto intError = mDevice.mComposer->setLayerDisplayFrame(mDisplayId, - mId, hwcRect); + auto intError = mComposer.setLayerDisplayFrame(mDisplayId, mId, hwcRect); return static_cast<Error>(intError); } Error Layer::setPlaneAlpha(float alpha) { - auto intError = mDevice.mComposer->setLayerPlaneAlpha(mDisplayId, - mId, alpha); + auto intError = mComposer.setLayerPlaneAlpha(mDisplayId, mId, alpha); return static_cast<Error>(intError); } Error Layer::setSidebandStream(const native_handle_t* stream) { - if (!mDevice.hasCapability(Capability::SidebandStream)) { + if (mCapabilities.count(Capability::SidebandStream) == 0) { ALOGE("Attempted to call setSidebandStream without checking that the " "device supports sideband streams"); return Error::Unsupported; } - auto intError = mDevice.mComposer->setLayerSidebandStream(mDisplayId, - mId, stream); + auto intError = mComposer.setLayerSidebandStream(mDisplayId, mId, stream); return static_cast<Error>(intError); } @@ -926,16 +838,14 @@ Error Layer::setSourceCrop(const FloatRect& crop) { Hwc2::IComposerClient::FRect hwcRect{ crop.left, crop.top, crop.right, crop.bottom}; - auto intError = mDevice.mComposer->setLayerSourceCrop(mDisplayId, - mId, hwcRect); + auto intError = mComposer.setLayerSourceCrop(mDisplayId, mId, hwcRect); return static_cast<Error>(intError); } Error Layer::setTransform(Transform transform) { auto intTransform = static_cast<Hwc2::Transform>(transform); - auto intError = mDevice.mComposer->setLayerTransform(mDisplayId, - mId, intTransform); + auto intError = mComposer.setLayerTransform(mDisplayId, mId, intTransform); return static_cast<Error>(intError); } @@ -950,20 +860,19 @@ Error Layer::setVisibleRegion(const Region& region) rectArray[rect].right, rectArray[rect].bottom}); } - auto intError = mDevice.mComposer->setLayerVisibleRegion(mDisplayId, - mId, hwcRects); + auto intError = mComposer.setLayerVisibleRegion(mDisplayId, mId, hwcRects); return static_cast<Error>(intError); } Error Layer::setZOrder(uint32_t z) { - auto intError = mDevice.mComposer->setLayerZOrder(mDisplayId, mId, z); + auto intError = mComposer.setLayerZOrder(mDisplayId, mId, z); return static_cast<Error>(intError); } Error Layer::setInfo(uint32_t type, uint32_t appId) { - auto intError = mDevice.mComposer->setLayerInfo(mDisplayId, mId, type, appId); + auto intError = mComposer.setLayerInfo(mDisplayId, mId, type, appId); return static_cast<Error>(intError); } diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 404bb284c5..fbe4c7ebed 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -53,24 +53,37 @@ namespace HWC2 { class Display; class Layer; -typedef std::function<void(std::shared_ptr<Display>, Connection)> - HotplugCallback; -typedef std::function<void(std::shared_ptr<Display>)> RefreshCallback; -typedef std::function<void(std::shared_ptr<Display>, nsecs_t)> VsyncCallback; +// Implement this interface to receive hardware composer events. +// +// These callback functions will generally be called on a hwbinder thread, but +// when first registering the callback the onHotplugReceived() function will +// immediately be called on the thread calling registerCallback(). +// +// All calls receive a sequenceId, which will be the value that was supplied to +// HWC2::Device::registerCallback(). It's used to help differentiate callbacks +// from different hardware composer instances. +class ComposerCallback { + public: + virtual void onHotplugReceived(int32_t sequenceId, hwc2_display_t display, + Connection connection, + bool primaryDisplay) = 0; + virtual void onRefreshReceived(int32_t sequenceId, + hwc2_display_t display) = 0; + virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display, + int64_t timestamp) = 0; + virtual ~ComposerCallback() = default; +}; // C++ Wrapper around hwc2_device_t. Load all functions pointers // and handle callback registration. class Device { public: - // useVrComposer is passed to the composer HAL. When true, the composer HAL - // will use the vr composer service, otherwise it uses the real hardware - // composer. - Device(bool useVrComposer); - ~Device(); + // Service name is expected to be 'default' or 'vr' for normal use. + // 'vr' will slightly modify the behavior of the mComposer. + Device(const std::string& serviceName); - friend class HWC2::Display; - friend class HWC2::Layer; + void registerCallback(ComposerCallback* callback, int32_t sequenceId); // Required by HWC2 @@ -82,27 +95,14 @@ public: uint32_t getMaxVirtualDisplayCount() const; Error createVirtualDisplay(uint32_t width, uint32_t height, - android_pixel_format_t* format, - std::shared_ptr<Display>* outDisplay); - - void registerHotplugCallback(HotplugCallback hotplug); - void registerRefreshCallback(RefreshCallback refresh); - void registerVsyncCallback(VsyncCallback vsync); + android_pixel_format_t* format, Display** outDisplay); + void destroyDisplay(hwc2_display_t displayId); - // For use by callbacks - - void callHotplug(std::shared_ptr<Display> display, Connection connected); - void callRefresh(std::shared_ptr<Display> display); - void callVsync(std::shared_ptr<Display> display, nsecs_t timestamp); + void onHotplug(hwc2_display_t displayId, Connection connection); // Other Device methods - // This will create a Display if one is not found, but it will not be marked - // as connected. This Display may be null if the display has been torn down - // but has not been removed from the map yet. - std::shared_ptr<Display> getDisplayById(hwc2_display_t id); - - bool hasCapability(HWC2::Capability capability) const; + Display* getDisplayById(hwc2_display_t id); android::Hwc2::Composer* getComposer() { return mComposer.get(); } @@ -110,37 +110,23 @@ private: // Initialization methods void loadCapabilities(); - void registerCallbacks(); - - // For use by Display - - void destroyVirtualDisplay(hwc2_display_t display); // Member variables std::unique_ptr<android::Hwc2::Composer> mComposer; - std::unordered_set<Capability> mCapabilities; - std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays; - - HotplugCallback mHotplug; - std::vector<std::pair<std::shared_ptr<Display>, Connection>> - mPendingHotplugs; - RefreshCallback mRefresh; - std::vector<std::shared_ptr<Display>> mPendingRefreshes; - VsyncCallback mVsync; - std::vector<std::pair<std::shared_ptr<Display>, nsecs_t>> mPendingVsyncs; + std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays; + bool mRegisteredCallback; }; // Convenience C++ class to access hwc2_device_t Display functions directly. -class Display : public std::enable_shared_from_this<Display> +class Display { public: - Display(Device& device, hwc2_display_t id); + Display(android::Hwc2::Composer& composer, + const std::unordered_set<Capability>& capabilities, + hwc2_display_t id, DisplayType type); ~Display(); - friend class HWC2::Device; - friend class HWC2::Layer; - class Config { public: @@ -213,12 +199,12 @@ public: // Required by HWC2 [[clang::warn_unused_result]] Error acceptChanges(); - [[clang::warn_unused_result]] Error createLayer( - std::shared_ptr<Layer>* outLayer); + [[clang::warn_unused_result]] Error createLayer(Layer** outLayer); + [[clang::warn_unused_result]] Error destroyLayer(Layer* layer); [[clang::warn_unused_result]] Error getActiveConfig( std::shared_ptr<const Config>* outConfig) const; [[clang::warn_unused_result]] Error getChangedCompositionTypes( - std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes); + std::unordered_map<Layer*, Composition>* outTypes); [[clang::warn_unused_result]] Error getColorModes( std::vector<android_color_mode_t>* outModes) const; @@ -228,14 +214,13 @@ public: [[clang::warn_unused_result]] Error getName(std::string* outName) const; [[clang::warn_unused_result]] Error getRequests( DisplayRequest* outDisplayRequests, - std::unordered_map<std::shared_ptr<Layer>, LayerRequest>* - outLayerRequests); + std::unordered_map<Layer*, LayerRequest>* outLayerRequests); [[clang::warn_unused_result]] Error getType(DisplayType* outType) const; [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const; [[clang::warn_unused_result]] Error getHdrCapabilities( std::unique_ptr<android::HdrCapabilities>* outCapabilities) const; [[clang::warn_unused_result]] Error getReleaseFences( - std::unordered_map<std::shared_ptr<Layer>, + std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const; [[clang::warn_unused_result]] Error present( android::sp<android::Fence>* outPresentFence); @@ -267,32 +252,31 @@ public: // Other Display methods - Device& getDevice() const { return mDevice; } hwc2_display_t getId() const { return mId; } bool isConnected() const { return mIsConnected; } + void setConnected(bool connected); // For use by Device only private: - // For use by Device - - void setConnected(bool connected) { mIsConnected = connected; } int32_t getAttribute(hwc2_config_t configId, Attribute attribute); void loadConfig(hwc2_config_t configId); void loadConfigs(); - // For use by Layer - void destroyLayer(hwc2_layer_t layerId); - // This may fail (and return a null pointer) if no layer with this ID exists // on this display - std::shared_ptr<Layer> getLayerById(hwc2_layer_t id) const; + Layer* getLayerById(hwc2_layer_t id) const; // Member variables - Device& mDevice; + // These are references to data owned by HWC2::Device, which will outlive + // this HWC2::Display, so these references are guaranteed to be valid for + // the lifetime of this object. + android::Hwc2::Composer& mComposer; + const std::unordered_set<Capability>& mCapabilities; + hwc2_display_t mId; bool mIsConnected; DisplayType mType; - std::unordered_map<hwc2_layer_t, std::weak_ptr<Layer>> mLayers; + std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers; // The ordering in this map matters, for getConfigs(), when it is // converted to a vector std::map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs; @@ -302,12 +286,18 @@ private: class Layer { public: - Layer(const std::shared_ptr<Display>& display, hwc2_layer_t id); + Layer(android::Hwc2::Composer& composer, + const std::unordered_set<Capability>& capabilities, + hwc2_display_t displayId, hwc2_layer_t layerId); ~Layer(); - bool isAbandoned() const { return mDisplay.expired(); } hwc2_layer_t getId() const { return mId; } + // Register a listener to be notified when the layer is destroyed. When the + // listener function is called, the Layer will be in the process of being + // destroyed, so it's not safe to call methods on it. + void setLayerDestroyedListener(std::function<void(Layer*)> listener); + [[clang::warn_unused_result]] Error setCursorPosition(int32_t x, int32_t y); [[clang::warn_unused_result]] Error setBuffer(uint32_t slot, const android::sp<android::GraphicBuffer>& buffer, @@ -334,11 +324,16 @@ public: [[clang::warn_unused_result]] Error setInfo(uint32_t type, uint32_t appId); private: - std::weak_ptr<Display> mDisplay; + // These are references to data owned by HWC2::Device, which will outlive + // this HWC2::Layer, so these references are guaranteed to be valid for + // the lifetime of this object. + android::Hwc2::Composer& mComposer; + const std::unordered_set<Capability>& mCapabilities; + hwc2_display_t mDisplayId; - Device& mDevice; hwc2_layer_t mId; android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN; + std::function<void(Layer*)> mLayerDestroyedListener; }; } // namespace HWC2 diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index ac2dde29d4..b096a3ae57 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -59,13 +59,12 @@ namespace android { // --------------------------------------------------------------------------- -HWComposer::HWComposer(bool useVrComposer) +HWComposer::HWComposer(const std::string& serviceName) : mHwcDevice(), mDisplayData(2), mFreeDisplaySlots(), mHwcDisplaySlots(), mCBContext(), - mEventHandler(nullptr), mVSyncCounts(), mRemainingHwcVirtualDisplays(0) { @@ -74,40 +73,15 @@ HWComposer::HWComposer(bool useVrComposer) mVSyncCounts[i] = 0; } - loadHwcModule(useVrComposer); + mHwcDevice = std::make_unique<HWC2::Device>(serviceName); + mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount(); } HWComposer::~HWComposer() {} -void HWComposer::setEventHandler(EventHandler* handler) -{ - if (handler == nullptr) { - ALOGE("setEventHandler: Rejected attempt to clear handler"); - return; - } - - bool wasNull = (mEventHandler == nullptr); - mEventHandler = handler; - - if (wasNull) { - auto hotplugHook = std::bind(&HWComposer::hotplug, this, - std::placeholders::_1, std::placeholders::_2); - mHwcDevice->registerHotplugCallback(hotplugHook); - auto invalidateHook = std::bind(&HWComposer::invalidate, this, - std::placeholders::_1); - mHwcDevice->registerRefreshCallback(invalidateHook); - auto vsyncHook = std::bind(&HWComposer::vsync, this, - std::placeholders::_1, std::placeholders::_2); - mHwcDevice->registerVsyncCallback(vsyncHook); - } -} - -// Load and prepare the hardware composer module. Sets mHwc. -void HWComposer::loadHwcModule(bool useVrComposer) -{ - ALOGV("loadHwcModule"); - mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer); - mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount(); +void HWComposer::registerCallback(HWC2::ComposerCallback* callback, + int32_t sequenceId) { + mHwcDevice->registerCallback(callback, sequenceId); } bool HWComposer::hasCapability(HWC2::Capability capability) const @@ -145,54 +119,51 @@ void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) { } } -void HWComposer::hotplug(const std::shared_ptr<HWC2::Display>& display, - HWC2::Connection connected) { - ALOGV("hotplug: %" PRIu64 ", %s", display->getId(), - to_string(connected).c_str()); - int32_t disp = 0; +void HWComposer::onHotplug(hwc2_display_t displayId, + HWC2::Connection connection) { + ALOGV("hotplug: %" PRIu64 ", %s", displayId, + to_string(connection).c_str()); + mHwcDevice->onHotplug(displayId, connection); if (!mDisplayData[0].hwcDisplay) { - ALOGE_IF(connected != HWC2::Connection::Connected, "Assumed primary" + ALOGE_IF(connection != HWC2::Connection::Connected, "Assumed primary" " display would be connected"); - mDisplayData[0].hwcDisplay = display; - mHwcDisplaySlots[display->getId()] = 0; - disp = DisplayDevice::DISPLAY_PRIMARY; + mDisplayData[0].hwcDisplay = mHwcDevice->getDisplayById(displayId); + mHwcDisplaySlots[displayId] = 0; } else { // Disconnect is handled through HWComposer::disconnectDisplay via // SurfaceFlinger's onHotplugReceived callback handling - if (connected == HWC2::Connection::Connected) { - mDisplayData[1].hwcDisplay = display; - mHwcDisplaySlots[display->getId()] = 1; + if (connection == HWC2::Connection::Connected) { + mDisplayData[1].hwcDisplay = mHwcDevice->getDisplayById(displayId); + mHwcDisplaySlots[displayId] = 1; } - disp = DisplayDevice::DISPLAY_EXTERNAL; } - mEventHandler->onHotplugReceived(this, disp, - connected == HWC2::Connection::Connected); -} - -void HWComposer::invalidate(const std::shared_ptr<HWC2::Display>& /*display*/) { - mEventHandler->onInvalidateReceived(this); } -void HWComposer::vsync(const std::shared_ptr<HWC2::Display>& display, - int64_t timestamp) { +bool HWComposer::onVsync(hwc2_display_t displayId, int64_t timestamp, + int32_t* outDisplay) { + auto display = mHwcDevice->getDisplayById(displayId); + if (!display) { + ALOGE("onVsync Failed to find display %" PRIu64, displayId); + return false; + } auto displayType = HWC2::DisplayType::Invalid; auto error = display->getType(&displayType); if (error != HWC2::Error::None) { - ALOGE("vsync: Failed to determine type of display %" PRIu64, + ALOGE("onVsync: Failed to determine type of display %" PRIu64, display->getId()); - return; + return false; } if (displayType == HWC2::DisplayType::Virtual) { ALOGE("Virtual display %" PRIu64 " passed to vsync callback", display->getId()); - return; + return false; } if (mHwcDisplaySlots.count(display->getId()) == 0) { ALOGE("Unknown physical display %" PRIu64 " passed to vsync callback", display->getId()); - return; + return false; } int32_t disp = mHwcDisplaySlots[display->getId()]; @@ -206,17 +177,21 @@ void HWComposer::vsync(const std::shared_ptr<HWC2::Display>& display, if (timestamp == mLastHwVSync[disp]) { ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")", timestamp); - return; + return false; } mLastHwVSync[disp] = timestamp; } + if (outDisplay) { + *outDisplay = disp; + } + char tag[16]; snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp); ATRACE_INT(tag, ++mVSyncCounts[disp] & 1); - mEventHandler->onVSyncReceived(this, disp, timestamp); + return true; } status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, @@ -235,7 +210,7 @@ status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, return INVALID_OPERATION; } - std::shared_ptr<HWC2::Display> display; + HWC2::Display* display; auto error = mHwcDevice->createVirtualDisplay(width, height, format, &display); if (error != HWC2::Error::None) { @@ -264,13 +239,13 @@ status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, return NO_ERROR; } -std::shared_ptr<HWC2::Layer> HWComposer::createLayer(int32_t displayId) { +HWC2::Layer* HWComposer::createLayer(int32_t displayId) { if (!isValidDisplay(displayId)) { ALOGE("Failed to create layer on invalid display %d", displayId); return nullptr; } auto display = mDisplayData[displayId].hwcDisplay; - std::shared_ptr<HWC2::Layer> layer; + HWC2::Layer* layer; auto error = display->createLayer(&layer); if (error != HWC2::Error::None) { ALOGE("Failed to create layer on display %d: %s (%d)", displayId, @@ -280,6 +255,19 @@ std::shared_ptr<HWC2::Layer> HWComposer::createLayer(int32_t displayId) { return layer; } +void HWComposer::destroyLayer(int32_t displayId, HWC2::Layer* layer) { + if (!isValidDisplay(displayId)) { + ALOGE("Failed to destroy layer on invalid display %d", displayId); + return; + } + auto display = mDisplayData[displayId].hwcDisplay; + auto error = display->destroyLayer(layer); + if (error != HWC2::Error::None) { + ALOGE("Failed to destroy layer on display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + } +} + nsecs_t HWComposer::getRefreshTimestamp(int32_t displayId) const { // this returns the last refresh timestamp. // if the last one is not available, we estimate it based on @@ -347,10 +335,8 @@ std::vector<android_color_mode_t> HWComposer::getColorModes(int32_t displayId) c displayId); return modes; } - const std::shared_ptr<HWC2::Display>& hwcDisplay = - mDisplayData[displayId].hwcDisplay; - auto error = hwcDisplay->getColorModes(&modes); + auto error = mDisplayData[displayId].hwcDisplay->getColorModes(&modes); if (error != HWC2::Error::None) { ALOGE("getColorModes failed for display %d: %s (%d)", displayId, to_string(error).c_str(), static_cast<int32_t>(error)); @@ -470,7 +456,7 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { return UNKNOWN_ERROR; } if (state == 1) { //Present Succeeded. - std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences; + std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; error = hwcDisplay->getReleaseFences(&releaseFences); displayData.releaseFences = std::move(releaseFences); displayData.lastPresentFence = outPresentFence; @@ -489,8 +475,7 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { return BAD_INDEX; } - std::unordered_map<std::shared_ptr<HWC2::Layer>, HWC2::Composition> - changedTypes; + std::unordered_map<HWC2::Layer*, HWC2::Composition> changedTypes; changedTypes.reserve(numTypes); error = hwcDisplay->getChangedCompositionTypes(&changedTypes); if (error != HWC2::Error::None) { @@ -502,8 +487,7 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { displayData.displayRequests = static_cast<HWC2::DisplayRequest>(0); - std::unordered_map<std::shared_ptr<HWC2::Layer>, HWC2::LayerRequest> - layerRequests; + std::unordered_map<HWC2::Layer*, HWC2::LayerRequest> layerRequests; layerRequests.reserve(numRequests); error = hwcDisplay->getRequests(&displayData.displayRequests, &layerRequests); @@ -597,7 +581,7 @@ sp<Fence> HWComposer::getPresentFence(int32_t displayId) const { } sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId, - const std::shared_ptr<HWC2::Layer>& layer) const { + HWC2::Layer* layer) const { if (!isValidDisplay(displayId)) { ALOGE("getLayerReleaseFence: Invalid display"); return Fence::NO_FENCE; @@ -638,7 +622,7 @@ status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) { return UNKNOWN_ERROR; } - std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences; + std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; error = hwcDisplay->getReleaseFences(&releaseFences); if (error != HWC2::Error::None) { ALOGE("presentAndGetReleaseFences: Failed to get release fences " @@ -786,6 +770,8 @@ void HWComposer::disconnectDisplay(int displayId) { auto hwcId = displayData.hwcDisplay->getId(); mHwcDisplaySlots.erase(hwcId); displayData.reset(); + + mHwcDevice->destroyDisplay(hwcId); } status_t HWComposer::setOutputBuffer(int32_t displayId, @@ -884,7 +870,7 @@ void HWComposer::dump(String8& result) const { HWComposer::DisplayData::DisplayData() : hasClientComposition(false), hasDeviceComposition(false), - hwcDisplay(), + hwcDisplay(nullptr), lastPresentFence(Fence::NO_FENCE), outbufHandle(nullptr), outbufAcquireFence(Fence::NO_FENCE), diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 7463362c35..3640bb5a98 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -65,24 +65,14 @@ class String8; class HWComposer { public: - class EventHandler { - friend class HWComposer; - virtual void onVSyncReceived( - HWComposer* composer, int32_t disp, nsecs_t timestamp) = 0; - virtual void onHotplugReceived(HWComposer* composer, int32_t disp, bool connected) = 0; - virtual void onInvalidateReceived(HWComposer* composer) = 0; - protected: - virtual ~EventHandler() {} - }; - - // useVrComposer is passed to the composer HAL. When true, the composer HAL - // will use the vr composer service, otherwise it uses the real hardware - // composer. - HWComposer(bool useVrComposer); + // Uses the named composer service. Valid choices for normal use + // are 'default' and 'vr'. + HWComposer(const std::string& serviceName); ~HWComposer(); - void setEventHandler(EventHandler* handler); + void registerCallback(HWC2::ComposerCallback* callback, + int32_t sequenceId); bool hasCapability(HWC2::Capability capability) const; @@ -92,7 +82,9 @@ public: android_pixel_format_t* format, int32_t* outId); // Attempts to create a new layer on this display - std::shared_ptr<HWC2::Layer> createLayer(int32_t displayId); + HWC2::Layer* createLayer(int32_t displayId); + // Destroy a previously created layer + void destroyLayer(int32_t displayId, HWC2::Layer* layer); // Asks the HAL what it can do status_t prepare(DisplayDevice& displayDevice); @@ -127,7 +119,7 @@ public: // Get last release fence for the given layer sp<Fence> getLayerReleaseFence(int32_t displayId, - const std::shared_ptr<HWC2::Layer>& layer) const; + HWC2::Layer* layer) const; // Set the output buffer and acquire fence for a virtual display. // Returns INVALID_OPERATION if displayId is not a virtual display. @@ -143,6 +135,12 @@ public: // Events handling --------------------------------------------------------- + // Returns true if successful, false otherwise. The + // DisplayDevice::DisplayType of the display is returned as an output param. + bool onVsync(hwc2_display_t displayId, int64_t timestamp, + int32_t* outDisplay); + void onHotplug(hwc2_display_t displayId, HWC2::Connection connection); + void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled); // Query display parameters. Pass in a display index (e.g. @@ -170,19 +168,11 @@ public: private: static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2; - void loadHwcModule(bool useVrComposer); - bool isValidDisplay(int32_t displayId) const; static void validateChange(HWC2::Composition from, HWC2::Composition to); struct cb_context; - void invalidate(const std::shared_ptr<HWC2::Display>& display); - void vsync(const std::shared_ptr<HWC2::Display>& display, - int64_t timestamp); - void hotplug(const std::shared_ptr<HWC2::Display>& display, - HWC2::Connection connected); - struct DisplayData { DisplayData(); ~DisplayData(); @@ -190,11 +180,10 @@ private: bool hasClientComposition; bool hasDeviceComposition; - std::shared_ptr<HWC2::Display> hwcDisplay; + HWC2::Display* hwcDisplay; HWC2::DisplayRequest displayRequests; sp<Fence> lastPresentFence; // signals when the last set op retires - std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> - releaseFences; + std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; buffer_handle_t outbufHandle; sp<Fence> outbufAcquireFence; mutable std::unordered_map<int32_t, @@ -215,7 +204,6 @@ private: mutable Mutex mDisplayLock; cb_context* mCBContext; - EventHandler* mEventHandler; size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; uint32_t mRemainingHwcVirtualDisplays; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index c129ae546c..1de5e48cb9 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -345,8 +345,9 @@ status_t VirtualDisplaySurface::dequeueBuffer(Source source, PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) { LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId); - status_t result = mSource[source]->dequeueBuffer(sslot, fence, - mSinkBufferWidth, mSinkBufferHeight, format, usage, nullptr); + status_t result = + mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight, + format, usage, nullptr, nullptr); if (result < 0) return result; int pslot = mapSource2ProducerSlot(source, *sslot); @@ -384,12 +385,13 @@ status_t VirtualDisplaySurface::dequeueBuffer(Source source, return result; } -status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, - uint32_t w, uint32_t h, PixelFormat format, uint64_t usage, - FrameEventHistoryDelta* outTimestamps) { +status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w, uint32_t h, + PixelFormat format, uint64_t usage, + uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) { if (mDisplayId < 0) { - return mSource[SOURCE_SINK]->dequeueBuffer( - pslot, fence, w, h, format, usage, outTimestamps); + return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge, + outTimestamps); } VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, @@ -449,6 +451,9 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, *pslot = mapSource2ProducerSlot(source, sslot); } } + if (outBufferAge) { + *outBufferAge = 0; + } return result; } @@ -622,6 +627,10 @@ status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const { return INVALID_OPERATION; } +status_t VirtualDisplaySurface::getConsumerUsage(uint64_t* outUsage) const { + return mSource[SOURCE_SINK]->getConsumerUsage(outUsage); +} + void VirtualDisplaySurface::updateQueueBufferOutput( QueueBufferOutput&& qbo) { mQueueBufferOutput = std::move(qbo); diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index 7f8b39b62b..1671aba1d8 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -104,9 +104,9 @@ private: virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf); virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers); virtual status_t setAsyncMode(bool async); - virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w, - uint32_t h, PixelFormat format, uint64_t usage, - FrameEventHistoryDelta *outTimestamps); + virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w, uint32_t h, + PixelFormat format, uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps); virtual status_t detachBuffer(int slot); virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence); @@ -130,6 +130,7 @@ private: virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) override; virtual status_t getUniqueId(uint64_t* outId) const override; + virtual status_t getConsumerUsage(uint64_t* outUsage) const override; // // Utility methods diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/EventControlThread.cpp index ee6e886d12..052a959724 100644 --- a/services/surfaceflinger/EventControlThread.cpp +++ b/services/surfaceflinger/EventControlThread.cpp @@ -31,34 +31,35 @@ void EventControlThread::setVsyncEnabled(bool enabled) { } bool EventControlThread::threadLoop() { - Mutex::Autolock lock(mMutex); - - bool vsyncEnabled = mVsyncEnabled; - -#ifdef USE_HWC2 - mFlinger->setVsyncEnabled(HWC_DISPLAY_PRIMARY, mVsyncEnabled); -#else - mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, - mVsyncEnabled); -#endif + enum class VsyncState {Unset, On, Off}; + auto currentVsyncState = VsyncState::Unset; while (true) { - status_t err = mCond.wait(mMutex); - if (err != NO_ERROR) { - ALOGE("error waiting for new events: %s (%d)", - strerror(-err), err); - return false; + auto requestedVsyncState = VsyncState::On; + { + Mutex::Autolock lock(mMutex); + requestedVsyncState = + mVsyncEnabled ? VsyncState::On : VsyncState::Off; + while (currentVsyncState == requestedVsyncState) { + status_t err = mCond.wait(mMutex); + if (err != NO_ERROR) { + ALOGE("error waiting for new events: %s (%d)", + strerror(-err), err); + return false; + } + requestedVsyncState = + mVsyncEnabled ? VsyncState::On : VsyncState::Off; + } } - if (vsyncEnabled != mVsyncEnabled) { + bool enable = requestedVsyncState == VsyncState::On; #ifdef USE_HWC2 - mFlinger->setVsyncEnabled(HWC_DISPLAY_PRIMARY, mVsyncEnabled); + mFlinger->setVsyncEnabled(HWC_DISPLAY_PRIMARY, enable); #else - mFlinger->eventControl(HWC_DISPLAY_PRIMARY, - SurfaceFlinger::EVENT_VSYNC, mVsyncEnabled); + mFlinger->eventControl(HWC_DISPLAY_PRIMARY, + SurfaceFlinger::EVENT_VSYNC, enable); #endif - vsyncEnabled = mVsyncEnabled; - } + currentVsyncState = requestedVsyncState; } return false; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 1b864fd2d8..e92565fd9c 100755 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -40,6 +40,7 @@ #include <gui/BufferItem.h> #include <gui/BufferQueue.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include "clz.h" @@ -200,6 +201,14 @@ Layer::~Layer() { } mFlinger->deleteTextureAsync(mTextureName); mFrameTracker.logAndResetStats(mName); + +#ifdef USE_HWC2 + if (!mHwcLayers.empty()) { + ALOGE("Found stale hardware composer layers when destroying " + "surface flinger layer %s", mName.string()); + destroyAllHwcLayers(); + } +#endif } // --------------------------------------------------------------------------- @@ -287,22 +296,29 @@ void Layer::onSidebandStreamChanged() { } } -// called with SurfaceFlinger::mStateLock from the drawing thread after -// the layer has been remove from the current state list (and just before -// it's removed from the drawing state list) -void Layer::onRemoved() { +void Layer::onRemovedFromCurrentState() { + // the layer is removed from SF mCurrentState to mLayersPendingRemoval + if (mCurrentState.zOrderRelativeOf != nullptr) { sp<Layer> strongRelative = mCurrentState.zOrderRelativeOf.promote(); if (strongRelative != nullptr) { strongRelative->removeZOrderRelative(this); + mFlinger->setTransactionFlags(eTraversalNeeded); } mCurrentState.zOrderRelativeOf = nullptr; } - mSurfaceFlingerConsumer->abandon(); + for (const auto& child : mCurrentChildren) { + child->onRemovedFromCurrentState(); + } +} +void Layer::onRemoved() { + // the layer is removed from SF mLayersPendingRemoval + + mSurfaceFlingerConsumer->abandon(); #ifdef USE_HWC2 - clearHwcLayers(); + destroyAllHwcLayers(); #endif for (const auto& child : mCurrentChildren) { @@ -363,6 +379,48 @@ sp<IGraphicBufferProducer> Layer::getProducer() const { // h/w composer set-up // --------------------------------------------------------------------------- +#ifdef USE_HWC2 +bool Layer::createHwcLayer(HWComposer* hwc, int32_t hwcId) { + LOG_ALWAYS_FATAL_IF(mHwcLayers.count(hwcId) != 0, + "Already have a layer for hwcId %d", hwcId); + HWC2::Layer* layer = hwc->createLayer(hwcId); + if (!layer) { + return false; + } + HWCInfo& hwcInfo = mHwcLayers[hwcId]; + hwcInfo.hwc = hwc; + hwcInfo.layer = layer; + layer->setLayerDestroyedListener( + [this, hwcId] (HWC2::Layer* /*layer*/){mHwcLayers.erase(hwcId);}); + return true; +} + +void Layer::destroyHwcLayer(int32_t hwcId) { + if (mHwcLayers.count(hwcId) == 0) { + return; + } + auto& hwcInfo = mHwcLayers[hwcId]; + LOG_ALWAYS_FATAL_IF(hwcInfo.layer == nullptr, + "Attempt to destroy null layer"); + LOG_ALWAYS_FATAL_IF(hwcInfo.hwc == nullptr, "Missing HWComposer"); + hwcInfo.hwc->destroyLayer(hwcId, hwcInfo.layer); + // The layer destroyed listener should have cleared the entry from + // mHwcLayers. Verify that. + LOG_ALWAYS_FATAL_IF(mHwcLayers.count(hwcId) != 0, + "Stale layer entry in mHwcLayers"); +} + +void Layer::destroyAllHwcLayers() { + size_t numLayers = mHwcLayers.size(); + for (size_t i = 0; i < numLayers; ++i) { + LOG_ALWAYS_FATAL_IF(mHwcLayers.empty(), "destroyAllHwcLayers failed"); + destroyHwcLayer(mHwcLayers.begin()->first); + } + LOG_ALWAYS_FATAL_IF(!mHwcLayers.empty(), + "All hardware composer layers should have been destroyed"); +} +#endif + Rect Layer::getContentCrop() const { // this is the crop rectangle that applies to the buffer // itself (as opposed to the window) @@ -1309,7 +1367,8 @@ bool Layer::headFenceHasSignaled() const { // able to be latched. To avoid this, grab this buffer anyway. return true; } - return mQueueItems[0].mFence->getSignalTime() != INT64_MAX; + return mQueueItems[0].mFenceTime->getSignalTime() != + Fence::SIGNAL_TIME_PENDING; #else return true; #endif @@ -2011,9 +2070,6 @@ bool Layer::onPreComposition(nsecs_t refreshStartTime) { bool Layer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, const CompositorTiming& compositorTiming) { - mAcquireTimeline.updateSignalTimes(); - mReleaseTimeline.updateSignalTimes(); - // mFrameLatencyNeeded is true when a new frame was latched for the // composition. if (!mFrameLatencyNeeded) @@ -2064,6 +2120,7 @@ void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) { auto releaseFenceTime = std::make_shared<FenceTime>( mSurfaceFlingerConsumer->getPrevFinalReleaseFence()); + mReleaseTimeline.updateSignalTimes(); mReleaseTimeline.push(releaseFenceTime); Mutex::Autolock lock(mFrameEventHistoryMutex); @@ -2254,6 +2311,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) #ifndef USE_HWC2 auto releaseFenceTime = std::make_shared<FenceTime>( mSurfaceFlingerConsumer->getPrevFinalReleaseFence()); + mReleaseTimeline.updateSignalTimes(); mReleaseTimeline.push(releaseFenceTime); if (mPreviousFrameNumber != 0) { mFrameEventHistory.addRelease(mPreviousFrameNumber, @@ -2363,69 +2421,51 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { // debugging // ---------------------------------------------------------------------------- -void Layer::dump(String8& result, Colorizer& colorizer) const -{ - const Layer::State& s(getDrawingState()); - - colorizer.colorize(result, Colorizer::GREEN); - result.appendFormat( - "+ %s %p (%s)\n", - getTypeId(), this, getName().string()); - colorizer.reset(result); - - s.activeTransparentRegion.dump(result, "transparentRegion"); - visibleRegion.dump(result, "visibleRegion"); - surfaceDamageRegion.dump(result, "surfaceDamageRegion"); - sp<Client> client(mClientRef.promote()); - PixelFormat pf = PIXEL_FORMAT_UNKNOWN; - const sp<GraphicBuffer>& buffer(getActiveBuffer()); - if (buffer != NULL) { - pf = buffer->getPixelFormat(); - } - - result.appendFormat( " " - "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), " - "crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), " - "isOpaque=%1d, invalidate=%1d, " - "dataspace=%s, pixelformat=%s " -#ifdef USE_HWC2 - "alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" -#else - "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" -#endif - " client=%p\n", - getLayerStack(), s.z, - s.active.transform.tx(), s.active.transform.ty(), - s.active.w, s.active.h, - s.crop.left, s.crop.top, - s.crop.right, s.crop.bottom, - s.finalCrop.left, s.finalCrop.top, - s.finalCrop.right, s.finalCrop.bottom, - isOpaque(s), contentDirty, - dataspaceDetails(getDataSpace()).c_str(), decodePixelFormat(pf).c_str(), - s.alpha, s.flags, - s.active.transform[0][0], s.active.transform[0][1], - s.active.transform[1][0], s.active.transform[1][1], - client.get()); - - sp<const GraphicBuffer> buf0(mActiveBuffer); - uint32_t w0=0, h0=0, s0=0, f0=0; - if (buf0 != 0) { - w0 = buf0->getWidth(); - h0 = buf0->getHeight(); - s0 = buf0->getStride(); - f0 = buf0->format; - } - result.appendFormat( - " " - "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X]," - " queued-frames=%d, mRefreshPending=%d\n", - mFormat, w0, h0, s0,f0, - mQueuedFrames, mRefreshPending); - - if (mSurfaceFlingerConsumer != 0) { - mSurfaceFlingerConsumer->dumpState(result, " "); +LayerDebugInfo Layer::getLayerDebugInfo() const { + LayerDebugInfo info; + const Layer::State& ds = getDrawingState(); + info.mName = getName(); + sp<Layer> parent = getParent(); + info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string()); + info.mType = String8(getTypeId()); + info.mTransparentRegion = ds.activeTransparentRegion; + info.mVisibleRegion = visibleRegion; + info.mSurfaceDamageRegion = surfaceDamageRegion; + info.mLayerStack = getLayerStack(); + info.mX = ds.active.transform.tx(); + info.mY = ds.active.transform.ty(); + info.mZ = ds.z; + info.mWidth = ds.active.w; + info.mHeight = ds.active.h; + info.mCrop = ds.crop; + info.mFinalCrop = ds.finalCrop; + info.mAlpha = ds.alpha; + info.mFlags = ds.flags; + info.mPixelFormat = getPixelFormat(); + info.mDataSpace = getDataSpace(); + info.mMatrix[0][0] = ds.active.transform[0][0]; + info.mMatrix[0][1] = ds.active.transform[0][1]; + info.mMatrix[1][0] = ds.active.transform[1][0]; + info.mMatrix[1][1] = ds.active.transform[1][1]; + { + sp<const GraphicBuffer> activeBuffer = getActiveBuffer(); + if (activeBuffer != 0) { + info.mActiveBufferWidth = activeBuffer->getWidth(); + info.mActiveBufferHeight = activeBuffer->getHeight(); + info.mActiveBufferStride = activeBuffer->getStride(); + info.mActiveBufferFormat = activeBuffer->format; + } else { + info.mActiveBufferWidth = 0; + info.mActiveBufferHeight = 0; + info.mActiveBufferStride = 0; + info.mActiveBufferFormat = 0; + } } + info.mNumQueuedFrames = getQueuedFrameCount(); + info.mRefreshPending = isBufferLatched(); + info.mIsOpaque = isOpaque(ds); + info.mContentDirty = contentDirty; + return info; } #ifdef USE_HWC2 @@ -2509,6 +2549,12 @@ void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta *outDelta) { Mutex::Autolock lock(mFrameEventHistoryMutex); if (newTimestamps) { + // If there are any unsignaled fences in the aquire timeline at this + // point, the previously queued frame hasn't been latched yet. Go ahead + // and try to get the signal time here so the syscall is taken out of + // the main thread's critical path. + mAcquireTimeline.updateSignalTimes(); + // Push the new fence after updating since it's likely still pending. mAcquireTimeline.push(newTimestamps->acquireFence); mFrameEventHistory.addQueue(*newTimestamps); } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2306d1a43a..f7b82e4fb7 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -60,6 +60,7 @@ class Colorizer; class DisplayDevice; class GraphicBuffer; class SurfaceFlinger; +class LayerDebugInfo; // --------------------------------------------------------------------------- @@ -419,8 +420,14 @@ public: bool isPotentialCursor() const { return mPotentialCursor;} /* - * called with the state lock when the surface is removed from the - * current list + * called with the state lock from a binder thread when the layer is + * removed from the current list to the pending removal list + */ + void onRemovedFromCurrentState(); + + /* + * called with the state lock from the main thread when the layer is + * removed from the pending removal list */ void onRemoved(); @@ -441,40 +448,26 @@ public: bool hasQueuedFrame() const { return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh; } + int32_t getQueuedFrameCount() const { return mQueuedFrames; } + #ifdef USE_HWC2 // ----------------------------------------------------------------------- + bool createHwcLayer(HWComposer* hwc, int32_t hwcId); + void destroyHwcLayer(int32_t hwcId); + void destroyAllHwcLayers(); + bool hasHwcLayer(int32_t hwcId) { - if (mHwcLayers.count(hwcId) == 0) { - return false; - } - if (mHwcLayers[hwcId].layer->isAbandoned()) { - ALOGI("Erasing abandoned layer %s on %d", mName.string(), hwcId); - mHwcLayers.erase(hwcId); - return false; - } - return true; + return mHwcLayers.count(hwcId) > 0; } - std::shared_ptr<HWC2::Layer> getHwcLayer(int32_t hwcId) { + HWC2::Layer* getHwcLayer(int32_t hwcId) { if (mHwcLayers.count(hwcId) == 0) { return nullptr; } return mHwcLayers[hwcId].layer; } - void setHwcLayer(int32_t hwcId, std::shared_ptr<HWC2::Layer>&& layer) { - if (layer) { - mHwcLayers[hwcId].layer = layer; - } else { - mHwcLayers.erase(hwcId); - } - } - - void clearHwcLayers() { - mHwcLayers.clear(); - } - #endif // ----------------------------------------------------------------------- @@ -489,9 +482,9 @@ public: inline const State& getCurrentState() const { return mCurrentState; } inline State& getCurrentState() { return mCurrentState; } + LayerDebugInfo getLayerDebugInfo() const; /* always call base class first */ - void dump(String8& result, Colorizer& colorizer) const; #ifdef USE_HWC2 static void miniDumpHeader(String8& result); void miniDump(String8& result, int32_t hwcId) const; @@ -689,6 +682,9 @@ public: sp<IGraphicBufferProducer> getProducer() const; const String8& getName() const; void notifyAvailableFrames(); + + PixelFormat getPixelFormat() const { return mFormat; } + private: // ----------------------------------------------------------------------- @@ -760,12 +756,14 @@ private: // HWC items, accessed from the main thread struct HWCInfo { HWCInfo() - : layer(), + : hwc(nullptr), + layer(nullptr), forceClientComposition(false), compositionType(HWC2::Composition::Invalid), clearClientTarget(false) {} - std::shared_ptr<HWC2::Layer> layer; + HWComposer* hwc; + HWC2::Layer* layer; bool forceClientComposition; HWC2::Composition compositionType; bool clearClientTarget; diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index e717632c0f..1a5a85e079 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -68,11 +68,11 @@ status_t MonitoredProducer::setAsyncMode(bool async) { return mProducer->setAsyncMode(async); } -status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence, - uint32_t w, uint32_t h, PixelFormat format, uint64_t usage, - FrameEventHistoryDelta* outTimestamps) { - return mProducer->dequeueBuffer( - slot, fence, w, h, format, usage, outTimestamps); +status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, + PixelFormat format, uint64_t usage, + uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) { + return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps); } status_t MonitoredProducer::detachBuffer(int slot) { @@ -158,6 +158,10 @@ status_t MonitoredProducer::getUniqueId(uint64_t* outId) const { return mProducer->getUniqueId(outId); } +status_t MonitoredProducer::getConsumerUsage(uint64_t* outUsage) const { + return mProducer->getConsumerUsage(outUsage); +} + IBinder* MonitoredProducer::onAsBinder() { return this; } diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h index 58b9bc4e2e..1246d142f3 100644 --- a/services/surfaceflinger/MonitoredProducer.h +++ b/services/surfaceflinger/MonitoredProducer.h @@ -39,9 +39,9 @@ public: virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers); virtual status_t setAsyncMode(bool async); - virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, - uint32_t h, PixelFormat format, uint64_t usage, - FrameEventHistoryDelta* outTimestamps); + virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, + PixelFormat format, uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps); virtual status_t detachBuffer(int slot); virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence); @@ -68,6 +68,7 @@ public: virtual status_t setAutoRefresh(bool autoRefresh) override; virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override; virtual status_t getUniqueId(uint64_t* outId) const override; + virtual status_t getConsumerUsage(uint64_t* outUsage) const override; // The Layer which created this producer, and on which queued Buffer's will be displayed. sp<Layer> getLayer() const; diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp index 7e5eda0edf..56e9ac07ad 100644 --- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp +++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp @@ -64,7 +64,7 @@ RenderEngine* RenderEngine::create(EGLDisplay display, int hwcFormat, uint32_t f "EGL_ANDROIDX_no_config_context") && !findExtension(eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS), "EGL_KHR_no_config_context")) { - config = chooseEglConfig(display, hwcFormat); + config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); } EGLint renderableType = 0; @@ -108,7 +108,7 @@ RenderEngine* RenderEngine::create(EGLDisplay display, int hwcFormat, uint32_t f EGLConfig dummyConfig = config; if (dummyConfig == EGL_NO_CONFIG) { - dummyConfig = chooseEglConfig(display, hwcFormat); + dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); } EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE }; EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs); @@ -406,7 +406,8 @@ static status_t selectEGLConfig(EGLDisplay display, EGLint format, return err; } -EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format) { +EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format, + bool logConfig) { status_t err; EGLConfig config; @@ -427,18 +428,20 @@ EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format) { } } - // print some debugging info - EGLint r,g,b,a; - eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); - eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); - eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); - eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); - ALOGI("EGL information:"); - ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); - ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); - ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); - ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); - ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + if (logConfig) { + // print some debugging info + EGLint r,g,b,a; + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); + ALOGI("EGL information:"); + ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); + ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); + ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); + ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + } return config; } diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h index 56f582755e..954457946e 100644 --- a/services/surfaceflinger/RenderEngine/RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/RenderEngine.h @@ -64,7 +64,7 @@ public: }; static RenderEngine* create(EGLDisplay display, int hwcFormat, uint32_t featureFlags); - static EGLConfig chooseEglConfig(EGLDisplay display, int format); + static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); void primeCache() const; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index d29d9c19e9..78ec4fd623 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -46,6 +46,7 @@ #include <gui/BufferQueue.h> #include <gui/GuiConfig.h> #include <gui/IDisplayEventConnection.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <ui/GraphicBufferAllocator.h> @@ -76,6 +77,7 @@ #include "MonitoredProducer.h" #include "SurfaceFlinger.h" +#include "DisplayHardware/ComposerHal.h" #include "DisplayHardware/FramebufferSurface.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/VirtualDisplaySurface.h" @@ -100,10 +102,24 @@ extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy namespace android { - using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; +namespace { +class ConditionalLock { +public: + ConditionalLock(Mutex& mutex, bool lock) : mMutex(mutex), mLocked(lock) { + if (lock) { + mMutex.lock(); + } + } + ~ConditionalLock() { if (mLocked) mMutex.unlock(); } +private: + Mutex& mMutex; + bool mLocked; +}; +} // namespace anonymous + // --------------------------------------------------------------------------- const String16 sHardwareTest("android.permission.HARDWARE_TEST"); @@ -123,6 +139,21 @@ bool SurfaceFlinger::useVrFlinger; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; bool SurfaceFlinger::hasWideColorDisplay; + +std::string getHwcServiceName() { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.hwc_service_name", value, "default"); + ALOGI("Using HWComposer service: '%s'", value); + return std::string(value); +} + +bool useTrebleTestingOverride() { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.treble_testing_override", value, "false"); + ALOGI("Treble testing override: '%s'", value); + return std::string(value) == "true"; +} + SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), mTransactionFlags(0), @@ -131,9 +162,7 @@ SurfaceFlinger::SurfaceFlinger() mLayersRemoved(false), mLayersAdded(false), mRepaintEverything(0), - mHwc(nullptr), - mRealHwc(nullptr), - mVrHwc(nullptr), + mHwcServiceName(getHwcServiceName()), mRenderEngine(nullptr), mBootTime(systemTime()), mBuiltinDisplays(), @@ -160,7 +189,9 @@ SurfaceFlinger::SurfaceFlinger() mTotalTime(0), mLastSwapTime(0), mNumLayers(0), - mVrFlingerRequestsDisplay(false) + mVrFlingerRequestsDisplay(false), + mMainThreadId(std::this_thread::get_id()), + mComposerSequenceId(0) { ALOGI("SurfaceFlinger is starting"); @@ -233,6 +264,15 @@ SurfaceFlinger::SurfaceFlinger() // but since /data may be encrypted, we need to wait until after vold // comes online to attempt to read the property. The property is // instead read after the boot animation + + if (useTrebleTestingOverride()) { + // Without the override SurfaceFlinger cannot connect to HIDL + // services that are not listed in the manifests. Considered + // deriving the setting from the set service name, but it + // would be brittle if the name that's not 'default' is used + // for production purposes later on. + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + } } void SurfaceFlinger::onFirstRef() @@ -547,54 +587,61 @@ void SurfaceFlinger::init() { ALOGI("Phase offest NS: %" PRId64 "", vsyncPhaseOffsetNs); - { // Autolock scope - Mutex::Autolock _l(mStateLock); + Mutex::Autolock _l(mStateLock); - // initialize EGL for the default display - mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(mEGLDisplay, NULL, NULL); - - // start the EventThread - sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync, - vsyncPhaseOffsetNs, true, "app"); - mEventThread = new EventThread(vsyncSrc, *this, false); - sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, - sfVsyncPhaseOffsetNs, true, "sf"); - mSFEventThread = new EventThread(sfVsyncSrc, *this, true); - mEventQueue.setEventThread(mSFEventThread); + // initialize EGL for the default display + mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(mEGLDisplay, NULL, NULL); - // set EventThread and SFEventThread to SCHED_FIFO to minimize jitter - struct sched_param param = {0}; - param.sched_priority = 2; - if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { - ALOGE("Couldn't set SCHED_FIFO for SFEventThread"); - } - if (sched_setscheduler(mEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { - ALOGE("Couldn't set SCHED_FIFO for EventThread"); - } + // start the EventThread + sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync, + vsyncPhaseOffsetNs, true, "app"); + mEventThread = new EventThread(vsyncSrc, *this, false); + sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, + sfVsyncPhaseOffsetNs, true, "sf"); + mSFEventThread = new EventThread(sfVsyncSrc, *this, true); + mEventQueue.setEventThread(mSFEventThread); - // Get a RenderEngine for the given display / config (can't fail) - mRenderEngine = RenderEngine::create(mEGLDisplay, - HAL_PIXEL_FORMAT_RGBA_8888, - hasWideColorDisplay ? RenderEngine::WIDE_COLOR_SUPPORT : 0); + // set EventThread and SFEventThread to SCHED_FIFO to minimize jitter + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for SFEventThread"); + } + if (sched_setscheduler(mEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for EventThread"); } - // Drop the state lock while we initialize the hardware composer. We drop - // the lock because on creation, it will call back into SurfaceFlinger to - // initialize the primary display. - LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay, - "Starting with vr flinger active is not currently supported."); - mRealHwc = new HWComposer(false); - mHwc = mRealHwc; - mHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this)); + // Get a RenderEngine for the given display / config (can't fail) + mRenderEngine = RenderEngine::create(mEGLDisplay, + HAL_PIXEL_FORMAT_RGBA_8888, + hasWideColorDisplay ? RenderEngine::WIDE_COLOR_SUPPORT : 0); - Mutex::Autolock _l(mStateLock); + // retrieve the EGL context that was selected/created + mEGLContext = mRenderEngine->getEGLContext(); + + LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT, + "couldn't create EGLContext"); + + LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay, + "Starting with vr flinger active is not currently supported."); + mHwc.reset(new HWComposer(mHwcServiceName)); + mHwc->registerCallback(this, mComposerSequenceId); if (useVrFlinger) { auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) { - ALOGI("VR request display mode: requestDisplay=%d", requestDisplay); - mVrFlingerRequestsDisplay = requestDisplay; - signalTransaction(); + // This callback is called from the vr flinger dispatch thread. We + // need to call signalTransaction(), which requires holding + // mStateLock when we're not on the main thread. Acquiring + // mStateLock from the vr flinger dispatch thread might trigger a + // deadlock in surface flinger (see b/66916578), so post a message + // to be handled on the main thread instead. + sp<LambdaMessage> message = new LambdaMessage([=]() { + ALOGI("VR request display mode: requestDisplay=%d", requestDisplay); + mVrFlingerRequestsDisplay = requestDisplay; + signalTransaction(); + }); + postMessageAsync(message); }; mVrFlinger = dvr::VrFlinger::Create(mHwc->getComposer(), vrFlingerRequestDisplayCallback); @@ -603,16 +650,6 @@ void SurfaceFlinger::init() { } } - // retrieve the EGL context that was selected/created - mEGLContext = mRenderEngine->getEGLContext(); - - LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT, - "couldn't create EGLContext"); - - // make the GLContext current so that we can create textures when creating - // Layers (which may happens before we render something) - getDefaultDisplayDeviceLocked()->makeCurrent(mEGLDisplay, mEGLContext); - mEventControlThread = new EventControlThread(this); mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY); @@ -691,6 +728,8 @@ status_t SurfaceFlinger::getSupportedFrameTimestamps( FrameEvent::DEQUEUE_READY, FrameEvent::RELEASE, }; + ConditionalLock _l(mStateLock, + std::this_thread::get_id() != mMainThreadId); if (!getHwComposer().hasCapability( HWC2::Capability::PresentFenceIsNotReliable)) { outSupported->push_back(FrameEvent::DISPLAY_PRESENT); @@ -738,6 +777,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, configs->clear(); + ConditionalLock _l(mStateLock, + std::this_thread::get_id() != mMainThreadId); for (const auto& hwConfig : getHwComposer().getConfigs(type)) { DisplayInfo info = DisplayInfo(); @@ -761,7 +802,7 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.density = density; // TODO: this needs to go away (currently needed only by webkit) - sp<const DisplayDevice> hw(getDefaultDisplayDevice()); + sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked()); info.orientation = hw->getOrientation(); } else { // TODO: where should this value come from? @@ -904,7 +945,12 @@ status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display, return type; } - std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(type); + std::vector<android_color_mode_t> modes; + { + ConditionalLock _l(mStateLock, + std::this_thread::get_id() != mMainThreadId); + modes = getHwComposer().getColorModes(type); + } outColorModes->clear(); std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes)); @@ -1045,6 +1091,33 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { return NO_ERROR; } +status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission(sDump, pid, uid)) { + ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + + // Try to acquire a lock for 1s, fail gracefully + const status_t err = mStateLock.timedLock(s2ns(1)); + const bool locked = (err == NO_ERROR); + if (!locked) { + ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err); + return TIMED_OUT; + } + + outLayers->clear(); + mCurrentState.traverseInZOrder([&](Layer* layer) { + outLayers->push_back(layer->getLayerDebugInfo()); + }); + + mStateLock.unlock(); + return NO_ERROR; +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( @@ -1155,11 +1228,16 @@ void SurfaceFlinger::resyncWithRateLimit() { sLastResyncAttempted = now; } -void SurfaceFlinger::onVSyncReceived(HWComposer* composer, int32_t type, - nsecs_t timestamp) { +void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, + hwc2_display_t displayId, int64_t timestamp) { Mutex::Autolock lock(mStateLock); - // Ignore any vsyncs from the non-active hardware composer. - if (composer != mHwc) { + // Ignore any vsyncs from a previous hardware composer. + if (sequenceId != mComposerSequenceId) { + return; + } + + int32_t type; + if (!mHwc->onVsync(displayId, timestamp, &type)) { return; } @@ -1167,7 +1245,7 @@ void SurfaceFlinger::onVSyncReceived(HWComposer* composer, int32_t type, { // Scope for the lock Mutex::Autolock _l(mHWVsyncLock); - if (type == 0 && mPrimaryHWVsyncEnabled) { + if (type == DisplayDevice::DISPLAY_PRIMARY && mPrimaryHWVsyncEnabled) { needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp); } } @@ -1185,7 +1263,7 @@ void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { } void SurfaceFlinger::createDefaultDisplayDevice() { - const int32_t type = DisplayDevice::DISPLAY_PRIMARY; + const DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_PRIMARY; wp<IBinder> token = mBuiltinDisplays[type]; // All non-virtual displays are currently considered secure. @@ -1220,28 +1298,49 @@ void SurfaceFlinger::createDefaultDisplayDevice() { } setActiveColorModeInternal(hw, defaultColorMode); hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN); -} -void SurfaceFlinger::onHotplugReceived(HWComposer* composer, int32_t disp, bool connected) { - ALOGV("onHotplugReceived(%d, %s)", disp, connected ? "true" : "false"); + // Add the primary display token to mDrawingState so we don't try to + // recreate the DisplayDevice for the primary display. + mDrawingState.displays.add(token, DisplayDeviceState(type, true)); - if (composer->isUsingVrComposer()) { - // We handle initializing the primary display device for the VR - // window manager hwc explicitly at the time of transition. - if (disp != DisplayDevice::DISPLAY_PRIMARY) { - ALOGE("External displays are not supported by the vr hardware composer."); - } - return; - } + // make the GLContext current so that we can create textures when creating + // Layers (which may happens before we render something) + hw->makeCurrent(mEGLDisplay, mEGLContext); +} - if (disp == DisplayDevice::DISPLAY_PRIMARY) { - Mutex::Autolock lock(mStateLock); - createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY); +void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, + hwc2_display_t display, HWC2::Connection connection, + bool primaryDisplay) { + ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s, %s)", + sequenceId, display, + connection == HWC2::Connection::Connected ? + "connected" : "disconnected", + primaryDisplay ? "primary" : "external"); + + // Only lock if we're not on the main thread. This function is normally + // called on a hwbinder thread, but for the primary display it's called on + // the main thread with the state lock already held, so don't attempt to + // acquire it here. + ConditionalLock lock(mStateLock, + std::this_thread::get_id() != mMainThreadId); + + if (primaryDisplay) { + mHwc->onHotplug(display, connection); + if (!mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY].get()) { + createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY); + } createDefaultDisplayDevice(); } else { + if (sequenceId != mComposerSequenceId) { + return; + } + if (mHwc->isUsingVrComposer()) { + ALOGE("External displays are not supported by the vr hardware composer."); + return; + } + mHwc->onHotplug(display, connection); auto type = DisplayDevice::DISPLAY_EXTERNAL; - Mutex::Autolock _l(mStateLock); - if (connected) { + if (connection == HWC2::Connection::Connected) { createBuiltinDisplayLocked(type); } else { mCurrentState.displays.removeItem(mBuiltinDisplays[type]); @@ -1253,46 +1352,31 @@ void SurfaceFlinger::onHotplugReceived(HWComposer* composer, int32_t disp, bool } } -void SurfaceFlinger::onInvalidateReceived(HWComposer* composer) { +void SurfaceFlinger::onRefreshReceived(int sequenceId, + hwc2_display_t /*display*/) { Mutex::Autolock lock(mStateLock); - if (composer == mHwc) { - repaintEverything(); - } else { - // This isn't from our current hardware composer. If it's a callback - // from the real composer, forward the refresh request to vr - // flinger. Otherwise ignore it. - if (!composer->isUsingVrComposer()) { - mVrFlinger->OnHardwareComposerRefresh(); - } + if (sequenceId != mComposerSequenceId) { + return; } + repaintEverythingLocked(); } void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) { ATRACE_CALL(); + Mutex::Autolock lock(mStateLock); getHwComposer().setVsyncEnabled(disp, enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable); } // Note: it is assumed the caller holds |mStateLock| when this is called -void SurfaceFlinger::resetHwcLocked() { +void SurfaceFlinger::resetDisplayState() { disableHardwareVsync(true); - clearHwcLayers(mDrawingState.layersSortedByZ); - clearHwcLayers(mCurrentState.layersSortedByZ); - for (size_t disp = 0; disp < mDisplays.size(); ++disp) { - clearHwcLayers(mDisplays[disp]->getVisibleLayersSortedByZ()); - } // Clear the drawing state so that the logic inside of // handleTransactionLocked will fire. It will determine the delta between // mCurrentState and mDrawingState and re-apply all changes when we make the // transition. mDrawingState.displays.clear(); - // Release virtual display hwcId during vr mode transition. - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - const sp<DisplayDevice>& displayDevice = mDisplays[displayId]; - if (displayDevice->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL) { - displayDevice->disconnect(getHwComposer()); - } - } + eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); mDisplays.clear(); } @@ -1304,57 +1388,54 @@ void SurfaceFlinger::updateVrFlinger() { return; } - if (vrFlingerRequestsDisplay && !mVrHwc) { - // Construct new HWComposer without holding any locks. - mVrHwc = new HWComposer(true); - - // Set up the event handlers. This step is neccessary to initialize the internal state of - // the hardware composer object properly. Our callbacks are designed such that if they are - // triggered between now and the point where the display is properly re-initialized, they - // will not have any effect, so this is safe to do here, before the lock is aquired. - mVrHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this)); - ALOGV("Vr HWC created"); + if (vrFlingerRequestsDisplay && !mHwc->getComposer()->isRemote()) { + ALOGE("Vr flinger is only supported for remote hardware composer" + " service connections. Ignoring request to transition to vr" + " flinger."); + mVrFlingerRequestsDisplay = false; + return; } Mutex::Autolock _l(mStateLock); - if (vrFlingerRequestsDisplay) { - resetHwcLocked(); - - mHwc = mVrHwc; - mVrFlinger->GrantDisplayOwnership(); + int currentDisplayPowerMode = getDisplayDeviceLocked( + mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY])->getPowerMode(); - } else { + if (!vrFlingerRequestsDisplay) { mVrFlinger->SeizeDisplayOwnership(); + } + + resetDisplayState(); + mHwc.reset(); // Delete the current instance before creating the new one + mHwc.reset(new HWComposer( + vrFlingerRequestsDisplay ? "vr" : mHwcServiceName)); + mHwc->registerCallback(this, ++mComposerSequenceId); - resetHwcLocked(); + LOG_ALWAYS_FATAL_IF(!mHwc->getComposer()->isRemote(), + "Switched to non-remote hardware composer"); - mHwc = mRealHwc; + if (vrFlingerRequestsDisplay) { + mVrFlinger->GrantDisplayOwnership(); + } else { enableHardwareVsync(); } mVisibleRegionsDirty = true; invalidateHwcGeometry(); - // Explicitly re-initialize the primary display. This is because some other - // parts of this class rely on the primary display always being available. - createDefaultDisplayDevice(); - // Re-enable default display. - sp<LambdaMessage> requestMessage = new LambdaMessage([&]() { - sp<DisplayDevice> hw(getDisplayDevice(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY])); - setPowerModeInternal(hw, HWC_POWER_MODE_NORMAL); - - // Reset the timing values to account for the period of the swapped in HWC - const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); - const nsecs_t period = activeConfig->getVsyncPeriod(); - mAnimFrameTracker.setDisplayRefreshPeriod(period); - - // Use phase of 0 since phase is not known. - // Use latency of 0, which will snap to the ideal latency. - setCompositorTimingSnapped(0, period, 0); - }); - postMessageAsync(requestMessage); + sp<DisplayDevice> hw(getDisplayDeviceLocked( + mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY])); + setPowerModeInternal(hw, currentDisplayPowerMode, /*stateLockHeld*/ true); + + // Reset the timing values to account for the period of the swapped in HWC + const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + const nsecs_t period = activeConfig->getVsyncPeriod(); + mAnimFrameTracker.setDisplayRefreshPeriod(period); + + // Use phase of 0 since phase is not known. + // Use latency of 0, which will snap to the ideal latency. + setCompositorTimingSnapped(0, period, 0); android_atomic_or(1, &mRepaintEverything); setTransactionFlags(eDisplayTransactionNeeded); @@ -1370,7 +1451,6 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { Fence::SIGNAL_TIME_PENDING); ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); if (mPropagateBackpressure && frameMissed) { - ALOGD("Backpressure trigger, skipping transaction & refresh!"); signalLayerUpdate(); break; } @@ -1572,6 +1652,7 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) // |mStateLock| not needed as we are on the main thread const sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked()); + mGlCompositionDoneTimeline.updateSignalTimes(); std::shared_ptr<FenceTime> glCompositionDoneFenceTime; if (mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) { glCompositionDoneFenceTime = @@ -1580,12 +1661,11 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) } else { glCompositionDoneFenceTime = FenceTime::NO_FENCE; } - mGlCompositionDoneTimeline.updateSignalTimes(); + mDisplayTimeline.updateSignalTimes(); sp<Fence> presentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY); auto presentFenceTime = std::make_shared<FenceTime>(presentFence); mDisplayTimeline.push(presentFenceTime); - mDisplayTimeline.updateSignalTimes(); nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0); nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod(); @@ -1610,8 +1690,8 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) } }); - if (presentFence->isValid()) { - if (mPrimaryDispSync.addPresentFence(presentFence)) { + if (presentFenceTime->isValid()) { + if (mPrimaryDispSync.addPresentFence(presentFenceTime)) { enableHardwareVsync(); } else { disableHardwareVsync(false); @@ -1691,15 +1771,14 @@ void SurfaceFlinger::rebuildLayerStacks() { } else { // Clear out the HWC layer if this layer was // previously visible, but no longer is - layer->setHwcLayer(displayDevice->getHwcDisplayId(), - nullptr); + layer->destroyHwcLayer( + displayDevice->getHwcDisplayId()); } } else { // WM changes displayDevice->layerStack upon sleep/awake. // Here we make sure we delete the HWC layers even if // WM changed their layer stack. - layer->setHwcLayer(displayDevice->getHwcDisplayId(), - nullptr); + layer->destroyHwcLayer(displayDevice->getHwcDisplayId()); } }); } @@ -1814,10 +1893,7 @@ void SurfaceFlinger::setUpHWComposer() { for (size_t i = 0; i < currentLayers.size(); i++) { const auto& layer = currentLayers[i]; if (!layer->hasHwcLayer(hwcId)) { - auto hwcLayer = mHwc->createLayer(hwcId); - if (hwcLayer) { - layer->setHwcLayer(hwcId, std::move(hwcLayer)); - } else { + if (!layer->createHwcLayer(mHwc.get(), hwcId)) { layer->forceClientComposition(hwcId); continue; } @@ -2098,7 +2174,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) if (state.surface != NULL) { // Allow VR composer to use virtual displays. - if (mUseHwcVirtualDisplays || mHwc == mVrHwc) { + if (mUseHwcVirtualDisplays || mHwc->isUsingVrComposer()) { int width = 0; int status = state.surface->query( NATIVE_WINDOW_WIDTH, &width); @@ -2775,6 +2851,7 @@ status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer, bool topLevelOnly) return NO_ERROR; } + layer->onRemovedFromCurrentState(); mLayersPendingRemoval.add(layer); mLayersRemoved = true; mNumLayers -= 1 + layer->getChildrenCount(); @@ -2972,7 +3049,10 @@ uint32_t SurfaceFlinger::setClientStateLocked( } } if (what & layer_state_t::eRelativeLayerChanged) { + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) { + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); flags |= eTransactionNeeded|eTraversalNeeded; } } @@ -3221,7 +3301,8 @@ void SurfaceFlinger::onInitializeDisplays() { d.height = 0; displays.add(d); setTransactionState(state, displays, 0); - setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL); + setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL, + /*stateLockHeld*/ false); const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); const nsecs_t period = activeConfig->getVsyncPeriod(); @@ -3247,7 +3328,7 @@ void SurfaceFlinger::initializeDisplays() { } void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, - int mode) { + int mode, bool stateLockHeld) { ALOGD("Set power mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(), this); int32_t type = hw->getDisplayType(); @@ -3264,7 +3345,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, } if (mInterceptor.isEnabled()) { - Mutex::Autolock _l(mStateLock); + ConditionalLock lock(mStateLock, !stateLockHeld); ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken()); if (idx < 0) { ALOGW("Surface Interceptor SavePowerMode: invalid display token"); @@ -3285,7 +3366,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, mVisibleRegionsDirty = true; mHasPoweredOff = true; - repaintEverything(); + repaintEverythingLocked(); struct sched_param param = {0}; param.sched_priority = 1; @@ -3350,7 +3431,8 @@ void SurfaceFlinger::setPowerMode(const sp<IBinder>& display, int mode) { ALOGW("Attempt to set power mode = %d for virtual display", mMode); } else { - mFlinger.setPowerModeInternal(hw, mMode); + mFlinger.setPowerModeInternal( + hw, mMode, /*stateLockHeld*/ false); } return true; } @@ -3695,7 +3777,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.appendFormat("Visible layers (count = %zu)\n", mNumLayers); colorizer.reset(result); mCurrentState.traverseInZOrder([&](Layer* layer) { - layer->dump(result, colorizer); + result.append(to_string(layer->getLayerDebugInfo()).c_str()); }); /* @@ -3928,6 +4010,7 @@ status_t SurfaceFlinger::onTransact( return NO_ERROR; } case 1005:{ // force transaction + Mutex::Autolock _l(mStateLock); setTransactionFlags( eTransactionNeeded| eDisplayTransactionNeeded| @@ -4064,11 +4147,17 @@ status_t SurfaceFlinger::onTransact( return err; } -void SurfaceFlinger::repaintEverything() { +void SurfaceFlinger::repaintEverythingLocked() { android_atomic_or(1, &mRepaintEverything); signalTransaction(); } +void SurfaceFlinger::repaintEverything() { + ConditionalLock _l(mStateLock, + std::this_thread::get_id() != mMainThreadId); + repaintEverythingLocked(); +} + // Checks that the requested width and height are valid and updates them to the display dimensions // if they are set to 0 static status_t updateDimensionsLocked(const sp<const DisplayDevice>& displayDevice, diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index acfad46526..1b77aafc58 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -60,13 +60,20 @@ #include "SurfaceInterceptor.h" #include "StartPropertySetThread.h" +#ifdef USE_HWC2 +#include "DisplayHardware/HWC2.h" #include "DisplayHardware/HWComposer.h" +#else +#include "DisplayHardware/HWComposer_hwc1.h" +#endif + #include "Effects/Daltonizer.h" #include <map> #include <mutex> #include <queue> #include <string> +#include <thread> #include <utility> namespace android { @@ -99,7 +106,11 @@ enum { class SurfaceFlinger : public BnSurfaceComposer, private IBinder::DeathRecipient, +#ifdef USE_HWC2 + private HWC2::ComposerCallback +#else private HWComposer::EventHandler +#endif { public: @@ -181,6 +192,8 @@ public: // force full composition on all displays void repaintEverything(); + // Can only be called from the main thread or with mStateLock held + void repaintEverythingLocked(); // returns the default Display sp<const DisplayDevice> getDefaultDisplayDevice() const { @@ -300,6 +313,7 @@ private: HdrCapabilities* outCapabilities) const; virtual status_t enableVSyncInjections(bool enable); virtual status_t injectVSync(nsecs_t when); + virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const; /* ------------------------------------------------------------------------ @@ -313,17 +327,28 @@ private: virtual void onFirstRef(); /* ------------------------------------------------------------------------ - * HWComposer::EventHandler interface + * HWC2::ComposerCallback / HWComposer::EventHandler interface */ - virtual void onVSyncReceived(HWComposer* composer, int type, nsecs_t timestamp); - virtual void onHotplugReceived(HWComposer* composer, int disp, bool connected); - virtual void onInvalidateReceived(HWComposer* composer); +#ifdef USE_HWC2 + void onVsyncReceived(int32_t sequenceId, hwc2_display_t display, + int64_t timestamp) override; + void onHotplugReceived(int32_t sequenceId, hwc2_display_t display, + HWC2::Connection connection, + bool primaryDisplay) override; + void onRefreshReceived(int32_t sequenceId, hwc2_display_t display) override; +#else + void onVSyncReceived(HWComposer* composer, int type, nsecs_t timestamp) override; + void onHotplugReceived(HWComposer* composer, int disp, bool connected) override; + void onInvalidateReceived(HWComposer* composer) override; +#endif /* ------------------------------------------------------------------------ * Message handling */ void waitForEvent(); + // Can only be called from the main thread or with mStateLock held void signalTransaction(); + // Can only be called from the main thread or with mStateLock held void signalLayerUpdate(); void signalRefresh(); @@ -332,7 +357,12 @@ private: // called on the main thread in response to setActiveConfig() void setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode); // called on the main thread in response to setPowerMode() +#ifdef USE_HWC2 + void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, + bool stateLockHeld); +#else void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode); +#endif // Called on the main thread in response to setActiveColorMode() void setActiveColorModeInternal(const sp<DisplayDevice>& hw, android_color_mode_t colorMode); @@ -361,6 +391,7 @@ private: */ uint32_t getTransactionFlags(uint32_t flags); uint32_t peekTransactionFlags(); + // Can only be called from the main thread or with mStateLock held uint32_t setTransactionFlags(uint32_t flags); void commitTransaction(); uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s); @@ -590,13 +621,7 @@ private: /* ------------------------------------------------------------------------ * VrFlinger */ - template<typename T> - void clearHwcLayers(const T& layers) { - for (size_t i = 0; i < layers.size(); ++i) { - layers[i]->clearHwcLayers(); - } - } - void resetHwcLocked(); + void resetDisplayState(); // Check to see if we should handoff to vr flinger. void updateVrFlinger(); @@ -623,12 +648,32 @@ private: // access must be protected by mInvalidateLock volatile int32_t mRepaintEverything; - // current, real and vr hardware composers. - HWComposer* mHwc; + // The current hardware composer interface. + // + // The following thread safety rules apply when accessing mHwc, either + // directly or via getHwComposer(): + // + // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc + // only when switching into and out of vr. Recreating mHwc must only be + // done on the main thread. + // + // 2. When accessing mHwc on the main thread, it's not necessary to acquire + // mStateLock. + // + // 3. When accessing mHwc on a thread other than the main thread, we always + // need to acquire mStateLock. This is because the main thread could be + // in the process of destroying the current mHwc instance. + // + // The above thread safety rules only apply to SurfaceFlinger.cpp. In + // SurfaceFlinger_hwc1.cpp we create mHwc at surface flinger init and never + // destroy it, so it's always safe to access mHwc from any thread without + // acquiring mStateLock. + std::unique_ptr<HWComposer> mHwc; + #ifdef USE_HWC2 - HWComposer* mRealHwc; - HWComposer* mVrHwc; + const std::string mHwcServiceName; // "default" for real use, something else for testing. #endif + // constant members (no synchronization needed for access) RenderEngine* mRenderEngine; nsecs_t mBootTime; @@ -642,10 +687,6 @@ private: EGLDisplay mEGLDisplay; sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES]; -#ifdef USE_HWC2 - std::unique_ptr<dvr::VrFlinger> mVrFlinger; -#endif - // Can only accessed from the main thread, these members // don't need synchronization State mDrawingState{LayerVector::StateSet::Drawing}; @@ -767,8 +808,14 @@ private: status_t CheckTransactCodeCredentials(uint32_t code); #ifdef USE_HWC2 + std::unique_ptr<dvr::VrFlinger> mVrFlinger; std::atomic<bool> mVrFlingerRequestsDisplay; static bool useVrFlinger; + std::thread::id mMainThreadId; + // The composer sequence id is a monotonically increasing integer that we + // use to differentiate callbacks from different hardware composer + // instances. Each hardware composer instance gets a different sequence id. + int32_t mComposerSequenceId; #endif float mSaturation = 1.0f; diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp index 11fce05b9c..1d6fbaf19b 100644 --- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -41,6 +41,7 @@ #include <gui/BufferQueue.h> #include <gui/GuiConfig.h> #include <gui/IDisplayEventConnection.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <ui/GraphicBufferAllocator.h> @@ -536,8 +537,8 @@ void SurfaceFlinger::init() { // Initialize the H/W composer object. There may or may not be an // actual hardware composer underneath. - mHwc = new HWComposer(this, - *static_cast<HWComposer::EventHandler *>(this)); + mHwc.reset(new HWComposer(this, + *static_cast<HWComposer::EventHandler *>(this))); // get a RenderEngine for the given display / config (can't fail) mRenderEngine = RenderEngine::create(mEGLDisplay, @@ -933,6 +934,34 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { return NO_ERROR; } +status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission(sDump, pid, uid)) { + ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + + // Try to acquire a lock for 1s, fail gracefully + status_t err = mStateLock.timedLock(s2ns(1)); + bool locked = (err == NO_ERROR); + if (!locked) { + ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err); + return TIMED_OUT; + } + + outLayers->clear(); + mCurrentState.traverseInZOrder([&](Layer* layer) { + outLayers->push_back(layer->getLayerDebugInfo()); + }); + + mStateLock.unlock(); + + return NO_ERROR; +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( @@ -1259,6 +1288,7 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) const HWComposer& hwc = getHwComposer(); const sp<const DisplayDevice> hw(getDefaultDisplayDevice()); + mGlCompositionDoneTimeline.updateSignalTimes(); std::shared_ptr<FenceTime> glCompositionDoneFenceTime; if (getHwComposer().hasGlesComposition(hw->getHwcDisplayId())) { glCompositionDoneFenceTime = @@ -1267,12 +1297,11 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) } else { glCompositionDoneFenceTime = FenceTime::NO_FENCE; } - mGlCompositionDoneTimeline.updateSignalTimes(); + mDisplayTimeline.updateSignalTimes(); sp<Fence> retireFence = mHwc->getDisplayFence(HWC_DISPLAY_PRIMARY); auto retireFenceTime = std::make_shared<FenceTime>(retireFence); mDisplayTimeline.push(retireFenceTime); - mDisplayTimeline.updateSignalTimes(); nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0); nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod(); @@ -1300,7 +1329,7 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) }); if (retireFence->isValid()) { - if (mPrimaryDispSync.addPresentFence(retireFence)) { + if (mPrimaryDispSync.addPresentFence(retireFenceTime)) { enableHardwareVsync(); } else { disableHardwareVsync(false); @@ -2384,6 +2413,7 @@ status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer, bool topLevelOnly) return NO_ERROR; } + layer->onRemovedFromCurrentState(); mLayersPendingRemoval.add(layer); mLayersRemoved = true; mNumLayers -= 1 + layer->getChildrenCount(); @@ -2578,6 +2608,14 @@ uint32_t SurfaceFlinger::setClientStateLocked( } } } + if (what & layer_state_t::eRelativeLayerChanged) { + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); + if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) { + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); + flags |= eTransactionNeeded|eTraversalNeeded; + } + } if (what & layer_state_t::eSizeChanged) { if (layer->setSize(s.w, s.h)) { flags |= eTraversalNeeded; @@ -3255,7 +3293,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.appendFormat("Visible layers (count = %zu)\n", mNumLayers); colorizer.reset(result); mCurrentState.traverseInZOrder([&](Layer* layer) { - layer->dump(result, colorizer); + result.append(to_string(layer->getLayerDebugInfo()).c_str()); }); /* diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter index 915b5cd3ca..6be708ad1c 100644 --- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter +++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter @@ -1,5 +1,5 @@ { "presubmit": { - "filter": "LayerUpdateTest.*:ChildLayerTest.*:SurfaceFlingerStress.*" + "filter": "LayerUpdateTest.*:ChildLayerTest.*:SurfaceFlingerStress.*:CropLatchingTest.*:GeometryLatchingTest.*" } }
\ No newline at end of file diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp index b7792c7852..4ce14f8d3a 100644 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -566,6 +566,15 @@ protected: sc->expectBGColor(127, 127); sc->expectBGColor(128, 128); } + + void EXPECT_RESIZE_STATE(const char* trace) { + SCOPED_TRACE(trace); + ScreenCapture::captureScreen(&sc); + // The FG is now resized too 128,128 at 64,64 + sc->expectFGColor(64, 64); + sc->expectFGColor(191, 191); + sc->expectBGColor(192, 192); + } }; TEST_F(CropLatchingTest, CropLatching) { @@ -666,15 +675,17 @@ TEST_F(CropLatchingTest, FinalCropLatchingRegressionForb37531386) { mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); SurfaceComposerClient::closeGlobalTransaction(true); + EXPECT_INITIAL_STATE("after setting crops with geometryAppliesWithResize"); + SurfaceComposerClient::openGlobalTransaction(); mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1)); SurfaceComposerClient::closeGlobalTransaction(true); - EXPECT_INITIAL_STATE("after setting crops with geometryAppliesWithResize"); + EXPECT_INITIAL_STATE("after setting another crop"); completeFGResize(); - EXPECT_INITIAL_STATE("after the resize finishes"); + EXPECT_RESIZE_STATE("after the resize finishes"); } TEST_F(LayerUpdateTest, DeferredTransactionTest) { diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp new file mode 100644 index 0000000000..94f3f2561a --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -0,0 +1,35 @@ +cc_test { + name: "sffakehwc_test", + srcs: [ + "FakeComposerClient.cpp", + "FakeComposerService.cpp", + "FakeComposerUtils.cpp", + "SFFakeHwc_test.cpp" + ], + shared_libs: [ + "libcutils", + "libutils", + "libbinder", + "libui", + "libgui", + "liblog", + "libnativewindow", + "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.mapper@2.0", + "libhwbinder", + "libhardware", + "libhidlbase", + "libsync", + "libfmq", + "libbase", + "libhidltransport" + ], + static_libs: [ + "libhwcomposer-client", + "libsurfaceflingerincludes", + "libtrace_proto", + "libgmock" + ], + tags: ["tests"], + test_suites: ["device-tests"] +}
\ No newline at end of file diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp new file mode 100644 index 0000000000..60916f3ab9 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp @@ -0,0 +1,613 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeComposer" + +#include "FakeComposerClient.h" + +#include <gui/SurfaceComposerClient.h> + +#include <log/log.h> + +#include <gtest/gtest.h> + +#include <inttypes.h> +#include <time.h> +#include <algorithm> +#include <condition_variable> +#include <iostream> +#include <mutex> +#include <set> +#include <thread> + +constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0); +constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1); + +using namespace sftest; + +using android::Condition; +using android::Mutex; + +using Clock = std::chrono::steady_clock; +using TimePoint = std::chrono::time_point<Clock>; + +namespace { + +// Internal state of a layer in the HWC API. +class LayerImpl { +public: + LayerImpl() = default; + + bool mValid = true; + RenderState mRenderState; + uint32_t mZ = 0; +}; + +// Struct for storing per frame rectangle state. Contains the render +// state shared to the test case. Basically a snapshot and a subset of +// LayerImpl sufficient to re-create the pixels of a layer for the +// frame. +struct FrameRect { +public: + FrameRect(Layer layer_, const RenderState& state, uint32_t z_) + : layer(layer_), renderState(state), z(z_) {} + + const Layer layer; + const RenderState renderState; + const uint32_t z; +}; + +// Collection of FrameRects forming one rendered frame. Could store +// related fences and other data in the future. +class Frame { +public: + Frame() = default; + std::vector<std::unique_ptr<FrameRect>> rectangles; +}; + +class DelayedEventGenerator { +public: + DelayedEventGenerator(std::function<void()> onTimerExpired) + : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {} + + ~DelayedEventGenerator() { + ALOGI("DelayedEventGenerator exiting."); + { + std::unique_lock<std::mutex> lock(mMutex); + mRunning = false; + mWakeups.clear(); + mCondition.notify_one(); + } + mThread.join(); + ALOGI("DelayedEventGenerator exited."); + } + + void wakeAfter(std::chrono::nanoseconds waitTime) { + std::unique_lock<std::mutex> lock(mMutex); + mWakeups.insert(Clock::now() + waitTime); + mCondition.notify_one(); + } + +private: + void loop() { + while (true) { + // Lock scope + { + std::unique_lock<std::mutex> lock(mMutex); + mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); }); + if (!mRunning && mWakeups.empty()) { + // This thread should only exit once the destructor has been called and all + // wakeups have been processed + return; + } + + // At this point, mWakeups will not be empty + + TimePoint target = *(mWakeups.begin()); + auto status = mCondition.wait_until(lock, target); + while (status == std::cv_status::no_timeout) { + // This was either a spurious wakeup or another wakeup was added, so grab the + // oldest point and wait again + target = *(mWakeups.begin()); + status = mCondition.wait_until(lock, target); + } + + // status must have been timeout, so we can finally clear this point + mWakeups.erase(target); + } + // Callback *without* locks! + mOnTimerExpired(); + } + } + + std::function<void()> mOnTimerExpired; + std::thread mThread; + std::mutex mMutex; + std::condition_variable mCondition; + bool mRunning = true; + std::set<TimePoint> mWakeups; +}; + +} // namespace + +FakeComposerClient::FakeComposerClient() + : mCallbacksOn(false), + mClient(nullptr), + mCurrentConfig(NULL_DISPLAY_CONFIG), + mVsyncEnabled(false), + mLayers(), + mDelayedEventGenerator( + std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })), + mSurfaceComposer(nullptr) {} + +FakeComposerClient::~FakeComposerClient() {} + +void FakeComposerClient::removeClient() { + ALOGV("removeClient"); + // TODO: Ahooga! Only thing current lifetime management choices in + // APIs make possible. Sad. + delete this; +} + +void FakeComposerClient::enableCallback(bool enable) { + ALOGV("enableCallback"); + mCallbacksOn = enable; + if (mCallbacksOn) { + mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED); + } +} + +void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) { + if (mCallbacksOn) { + mClient->onHotplug(display, state); + } +} + +uint32_t FakeComposerClient::getMaxVirtualDisplayCount() { + ALOGV("getMaxVirtualDisplayCount"); + return 1; +} + +Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/, + PixelFormat* /*format*/, Display* /*outDisplay*/) { + ALOGV("createVirtualDisplay"); + return Error::NONE; +} + +Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) { + ALOGV("destroyVirtualDisplay"); + return Error::NONE; +} + +Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) { + ALOGV("createLayer"); + *outLayer = mLayers.size(); + auto newLayer = std::make_unique<LayerImpl>(); + mLayers.push_back(std::move(newLayer)); + return Error::NONE; +} + +Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) { + ALOGV("destroyLayer"); + mLayers[layer]->mValid = false; + return Error::NONE; +} + +Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) { + ALOGV("getActiveConfig"); + + // TODO Assert outConfig != nullptr + + // TODO This is my reading of the + // IComposerClient::getActiveConfig, but returning BAD_CONFIG + // seems to not fit SurfaceFlinger plans. See version 2 below. + // if (mCurrentConfig == NULL_DISPLAY_CONFIG) { + // return Error::BAD_CONFIG; + // } + //*outConfig = mCurrentConfig; + *outConfig = 1; // Very special config for you my friend + return Error::NONE; +} + +Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/, + uint32_t /*height*/, PixelFormat /*format*/, + Dataspace /*dataspace*/) { + ALOGV("getClientTargetSupport"); + return Error::NONE; +} + +Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) { + ALOGV("getColorModes"); + return Error::NONE; +} + +Error FakeComposerClient::getDisplayAttribute(Display display, Config config, + IComposerClient::Attribute attribute, + int32_t* outValue) { + ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display), + static_cast<int>(config), static_cast<int>(attribute), outValue); + + // TODO: SOOO much fun to be had with these alone + switch (attribute) { + case IComposerClient::Attribute::WIDTH: + *outValue = 1920; + break; + case IComposerClient::Attribute::HEIGHT: + *outValue = 1080; + break; + case IComposerClient::Attribute::VSYNC_PERIOD: + *outValue = 1666666666; + break; // TOOD: Tests break down if lowered to 16ms? + case IComposerClient::Attribute::DPI_X: + *outValue = 240; + break; + case IComposerClient::Attribute::DPI_Y: + *outValue = 240; + break; + default: + LOG_ALWAYS_FATAL("Say what!?! New attribute"); + } + + return Error::NONE; +} + +Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) { + ALOGV("getDisplayConfigs"); + // TODO assert display == 1, outConfigs != nullptr + + outConfigs->resize(1); + (*outConfigs)[0] = 1; + + return Error::NONE; +} + +Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) { + ALOGV("getDisplayName"); + return Error::NONE; +} + +Error FakeComposerClient::getDisplayType(Display /*display*/, + IComposerClient::DisplayType* outType) { + ALOGV("getDisplayType"); + // TODO: This setting nothing on the output had no effect on initial trials. Is first display + // assumed to be physical? + *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL); + return Error::NONE; +} + +Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) { + ALOGV("getDozeSupport"); + return Error::NONE; +} + +Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/, + float* /*outMaxLuminance*/, + float* /*outMaxAverageLuminance*/, + float* /*outMinLuminance*/) { + ALOGV("getHdrCapabilities"); + return Error::NONE; +} + +Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) { + ALOGV("setActiveConfig"); + mCurrentConfig = config; + return Error::NONE; +} + +Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) { + ALOGV("setColorMode"); + return Error::NONE; +} + +Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) { + ALOGV("setPowerMode"); + return Error::NONE; +} + +Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) { + mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE); + ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE"); + return Error::NONE; +} + +Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/, + int32_t /*hint*/) { + ALOGV("setColorTransform"); + return Error::NONE; +} + +Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/, + int32_t /*acquireFence*/, int32_t /*dataspace*/, + const std::vector<hwc_rect_t>& /*damage*/) { + ALOGV("setClientTarget"); + return Error::NONE; +} + +Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/, + int32_t /*releaseFence*/) { + ALOGV("setOutputBuffer"); + return Error::NONE; +} + +Error FakeComposerClient::validateDisplay( + Display /*display*/, std::vector<Layer>* /*outChangedLayers*/, + std::vector<IComposerClient::Composition>* /*outCompositionTypes*/, + uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/, + std::vector<uint32_t>* /*outRequestMasks*/) { + ALOGV("validateDisplay"); + // TODO: Assume touching nothing means All Korrekt! + return Error::NONE; +} + +Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) { + ALOGV("acceptDisplayChanges"); + // Didn't ask for changes because software is omnipotent. + return Error::NONE; +} + +bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) { + return a->z <= b->z; +} + +Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/, + std::vector<Layer>* /*outLayers*/, + std::vector<int32_t>* /*outReleaseFences*/) { + ALOGV("presentDisplay"); + // TODO Leaving layers and their fences out for now. Doing so + // means that we've already processed everything. Important to + // test that the fences are respected, though. (How?) + + std::unique_ptr<Frame> newFrame(new Frame); + for (uint64_t layer = 0; layer < mLayers.size(); layer++) { + const LayerImpl& layerImpl = *mLayers[layer]; + + if (!layerImpl.mValid) continue; + + auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ); + newFrame->rectangles.push_back(std::move(rect)); + } + std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering); + { + Mutex::Autolock _l(mStateMutex); + mFrames.push_back(std::move(newFrame)); + mFramesAvailable.broadcast(); + } + return Error::NONE; +} + +Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/, + int32_t /*x*/, int32_t /*y*/) { + ALOGV("setLayerCursorPosition"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer, + int32_t acquireFence) { + ALOGV("setLayerBuffer"); + LayerImpl& l = getLayerImpl(layer); + if (buffer != l.mRenderState.mBuffer) { + l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not? + } + l.mRenderState.mBuffer = buffer; + l.mRenderState.mAcquireFence = acquireFence; + + return Error::NONE; +} + +Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/, + const std::vector<hwc_rect_t>& /*damage*/) { + ALOGV("setLayerSurfaceDamage"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) { + ALOGV("setLayerBlendMode"); + getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode); + return Error::NONE; +} + +Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer, + IComposerClient::Color color) { + ALOGV("setLayerColor"); + getLayerImpl(layer).mRenderState.mLayerColor.r = color.r; + getLayerImpl(layer).mRenderState.mLayerColor.g = color.g; + getLayerImpl(layer).mRenderState.mLayerColor.b = color.b; + getLayerImpl(layer).mRenderState.mLayerColor.a = color.a; + return Error::NONE; +} + +Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/, + int32_t /*type*/) { + ALOGV("setLayerCompositionType"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/, + int32_t /*dataspace*/) { + ALOGV("setLayerDataspace"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer, + const hwc_rect_t& frame) { + ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right, + frame.bottom); + getLayerImpl(layer).mRenderState.mDisplayFrame = frame; + return Error::NONE; +} + +Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) { + ALOGV("setLayerPlaneAlpha"); + getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha; + return Error::NONE; +} + +Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/, + buffer_handle_t /*stream*/) { + ALOGV("setLayerSidebandStream"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer, + const hwc_frect_t& crop) { + ALOGV("setLayerSourceCrop"); + getLayerImpl(layer).mRenderState.mSourceCrop = crop; + return Error::NONE; +} + +Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) { + ALOGV("setLayerTransform"); + getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform); + return Error::NONE; +} + +Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer, + const std::vector<hwc_rect_t>& visible) { + ALOGV("setLayerVisibleRegion"); + getLayerImpl(layer).mRenderState.mVisibleRegion = visible; + return Error::NONE; +} + +Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) { + ALOGV("setLayerZOrder"); + getLayerImpl(layer).mZ = z; + return Error::NONE; +} + +////////////////////////////////////////////////////////////////// + +void FakeComposerClient::setClient(ComposerClient* client) { + mClient = client; +} + +void FakeComposerClient::requestVSync(uint64_t vsyncTime) { + if (mCallbacksOn) { + uint64_t timestamp = vsyncTime; + ALOGV("Vsync"); + if (timestamp == 0) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; + } + if (mSurfaceComposer != nullptr) { + mSurfaceComposer->injectVSync(timestamp); + } else { + mClient->onVsync(DEFAULT_DISPLAY, timestamp); + } + } +} + +void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) { + mDelayedEventGenerator->wakeAfter(wait); +} + +LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) { + // TODO Change these to an internal state check that can be + // invoked from the gtest? GTest macros do not seem all that safe + // when used outside the test class + EXPECT_GE(handle, static_cast<Layer>(0)); + EXPECT_LT(handle, mLayers.size()); + return *(mLayers[handle]); +} + +int FakeComposerClient::getFrameCount() const { + return mFrames.size(); +} + +static std::vector<RenderState> extractRenderState( + const std::vector<std::unique_ptr<FrameRect>>& internalRects) { + std::vector<RenderState> result; + result.reserve(internalRects.size()); + for (const std::unique_ptr<FrameRect>& rect : internalRects) { + result.push_back(rect->renderState); + } + return result; +} + +std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const { + Mutex::Autolock _l(mStateMutex); + return extractRenderState(mFrames[frame]->rectangles); +} + +std::vector<RenderState> FakeComposerClient::getLatestFrame() const { + Mutex::Autolock _l(mStateMutex); + return extractRenderState(mFrames[mFrames.size() - 1]->rectangles); +} + +void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) { + int currentFrame = 0; + { + Mutex::Autolock _l(mStateMutex); // I hope this is ok... + currentFrame = static_cast<int>(mFrames.size()); + requestVSync(); + } + waitUntilFrame(currentFrame + 1, maxWait); +} + +void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const { + Mutex::Autolock _l(mStateMutex); + while (mFrames.size() < static_cast<size_t>(targetFrame)) { + android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count()); + if (result == android::TIMED_OUT) { + ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame, + mFrames.size(), maxWait.count()); + return; + } + } +} + +void FakeComposerClient::clearFrames() { + Mutex::Autolock _l(mStateMutex); + mFrames.clear(); + for (const std::unique_ptr<LayerImpl>& layer : mLayers) { + if (layer->mValid) { + layer->mRenderState.mSwapCount = 0; + } + } +} + +void FakeComposerClient::onSurfaceFlingerStart() { + mSurfaceComposer == nullptr; + do { + mSurfaceComposer = new android::SurfaceComposerClient; + android::status_t initResult = mSurfaceComposer->initCheck(); + if (initResult != android::NO_ERROR) { + ALOGD("Init result: %d", initResult); + mSurfaceComposer = nullptr; + std::this_thread::sleep_for(10ms); + } + } while (mSurfaceComposer == nullptr); + ALOGD("SurfaceComposerClient created"); + mSurfaceComposer->enableVSyncInjections(true); +} + +void FakeComposerClient::onSurfaceFlingerStop() { + mSurfaceComposer->dispose(); + mSurfaceComposer.clear(); +} + +// Includes destroyed layers, stored in order of creation. +int FakeComposerClient::getLayerCount() const { + return mLayers.size(); +} + +Layer FakeComposerClient::getLayer(size_t index) const { + // NOTE: If/when passing calls through to actual implementation, + // this might get more involving. + return static_cast<Layer>(index); +} diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h new file mode 100644 index 0000000000..294abb2c59 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h @@ -0,0 +1,146 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "ComposerClient.h" +#include "RenderState.h" + +#include <utils/Condition.h> + +#include <chrono> + +using namespace android::hardware::graphics::composer::V2_1; +using namespace android::hardware::graphics::composer::V2_1::implementation; +using namespace android::hardware; +using namespace std::chrono_literals; + +namespace { +class LayerImpl; +class Frame; +class DelayedEventGenerator; +} // namespace + +namespace android { +class SurfaceComposerClient; +} // namespace android + +namespace sftest { + +class FakeComposerClient : public ComposerBase { +public: + FakeComposerClient(); + virtual ~FakeComposerClient(); + + void removeClient() override; + void enableCallback(bool enable) override; + uint32_t getMaxVirtualDisplayCount() override; + Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, + Display* outDisplay) override; + Error destroyVirtualDisplay(Display display) override; + Error createLayer(Display display, Layer* outLayer) override; + Error destroyLayer(Display display, Layer layer) override; + + Error getActiveConfig(Display display, Config* outConfig) override; + Error getClientTargetSupport(Display display, uint32_t width, uint32_t height, + PixelFormat format, Dataspace dataspace) override; + Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override; + Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute, + int32_t* outValue) override; + Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override; + Error getDisplayName(Display display, hidl_string* outName) override; + Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override; + Error getDozeSupport(Display display, bool* outSupport) override; + Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance, + float* outMaxAverageLuminance, float* outMinLuminance) override; + + Error setActiveConfig(Display display, Config config) override; + Error setColorMode(Display display, ColorMode mode) override; + Error setPowerMode(Display display, IComposerClient::PowerMode mode) override; + Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override; + + Error setColorTransform(Display display, const float* matrix, int32_t hint) override; + Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence, + int32_t dataspace, const std::vector<hwc_rect_t>& damage) override; + Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override; + Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers, + std::vector<IComposerClient::Composition>* outCompositionTypes, + uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, + std::vector<uint32_t>* outRequestMasks) override; + Error acceptDisplayChanges(Display display) override; + Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers, + std::vector<int32_t>* outReleaseFences) override; + + Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override; + Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer, + int32_t acquireFence) override; + Error setLayerSurfaceDamage(Display display, Layer layer, + const std::vector<hwc_rect_t>& damage) override; + Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override; + Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override; + Error setLayerCompositionType(Display display, Layer layer, int32_t type) override; + Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override; + Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override; + Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override; + Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override; + Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override; + Error setLayerTransform(Display display, Layer layer, int32_t transform) override; + Error setLayerVisibleRegion(Display display, Layer layer, + const std::vector<hwc_rect_t>& visible) override; + Error setLayerZOrder(Display display, Layer layer, uint32_t z) override; + + void setClient(ComposerClient* client); + + void requestVSync(uint64_t vsyncTime = 0); + // We don't want tests hanging, so always use a timeout. Remember + // to always check the number of frames with test ASSERT_! + // Wait until next frame is rendered after requesting vsync. + void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms); + void runVSyncAfter(std::chrono::nanoseconds wait); + + int getFrameCount() const; + // We don't want tests hanging, so always use a timeout. Remember + // to always check the number of frames with test ASSERT_! + void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const; + std::vector<RenderState> getFrameRects(int frame) const; + std::vector<RenderState> getLatestFrame() const; + void clearFrames(); + + void onSurfaceFlingerStart(); + void onSurfaceFlingerStop(); + + int getLayerCount() const; + Layer getLayer(size_t index) const; + + void hotplugDisplay(Display display, IComposerCallback::Connection state); + +private: + LayerImpl& getLayerImpl(Layer handle); + + bool mCallbacksOn; + ComposerClient* mClient; + Config mCurrentConfig; + bool mVsyncEnabled; + std::vector<std::unique_ptr<LayerImpl>> mLayers; + std::vector<std::unique_ptr<Frame>> mFrames; + // Using a pointer to hide the implementation into the CPP file. + std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator; + android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections + mutable android::Mutex mStateMutex; + mutable android::Condition mFramesAvailable; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp new file mode 100644 index 0000000000..c411604587 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeHwcService" +#include <log/log.h> + +#include "FakeComposerService.h" + +using namespace android::hardware; + +namespace sftest { + +FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {} + +FakeComposerService::~FakeComposerService() { + ALOGI("Maybe killing client %p", mClient.get()); + // Rely on sp to kill the client. +} + +Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) { + ALOGI("FakeComposerService::getCapabilities"); + hidl_cb(hidl_vec<Capability>()); + return Void(); +} + +Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { + ALOGI("FakeComposerService::dumpDebugInfo"); + hidl_cb(hidl_string()); + return Void(); +} + +Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) { + ALOGI("FakeComposerService::createClient %p", mClient.get()); + mClient->initialize(); + hidl_cb(Error::NONE, mClient); + return Void(); +} + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h new file mode 100644 index 0000000000..520408496f --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "ComposerClient.h" + +using namespace android::hardware::graphics::composer::V2_1; +using namespace android::hardware::graphics::composer::V2_1::implementation; +using android::hardware::Return; + +namespace sftest { + +class FakeComposerService : public IComposer { +public: + FakeComposerService(android::sp<ComposerClient>& client); + virtual ~FakeComposerService(); + + Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; + Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; + Return<void> createClient(createClient_cb hidl_cb) override; + +private: + android::sp<ComposerClient> mClient; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp new file mode 100644 index 0000000000..51956ec970 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeHwcUtil" +#include <log/log.h> + +#include "FakeComposerUtils.h" +#include "RenderState.h" + +#include "SurfaceFlinger.h" // Get the name of the service... + +#include <binder/IServiceManager.h> + +#include <cutils/properties.h> + +#include <iomanip> +#include <thread> + +using android::String16; +using android::sp; +using namespace std::chrono_literals; +using namespace sftest; +using std::setw; + +namespace sftest { + +// clang-format off +inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) { + os << std::fixed << std::setprecision(1) << "(" + << setw(align) << sourceRect.left << setw(0) << "," + << setw(align) << sourceRect.top << setw(0) << "," + << setw(align) << sourceRect.right << setw(0) << "," + << setw(align) << sourceRect.bottom << setw(0) << ")"; +} + +inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) { + os << "(" + << setw(align) << displayRect.left << setw(0) << "," + << setw(align) << displayRect.top << setw(0) << "," + << setw(align) << displayRect.right << setw(0) << "," + << setw(align) << displayRect.bottom << setw(0) << ")"; +} +// clang-format on + +inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) { + printSourceRectAligned(os, state.mSourceCrop, 7); + os << "->"; + printDisplayRectAligned(os, state.mDisplayFrame, 5); + return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3) + << state.mPlaneAlpha << " Xform:" << state.mTransform; +} + +// Helper for verifying the parts of the RenderState +template <typename T> +bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val, + const char* name) { + if (ref != val) { + message = message << "Expected " << name << ":" << ref << ", got:" << val << "."; + return false; + } + return true; +} + +::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) { + // TODO: Message could start as success and be assigned as failure. + // Only problem is that utility assumes it to be failure and just adds stuff. Would + // need still special case the initial failure in the utility? + // TODO: ... or would it be possible to break this back to gtest primitives? + ::testing::AssertionResult message = ::testing::AssertionFailure(); + bool passes = true; + + // The work here is mostly about providing good log strings for differences + passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame"); + passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha"); + passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count"); + passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop"); + // ... add more + if (passes) { + return ::testing::AssertionSuccess(); + } + return message; +} + +::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, + const std::vector<RenderState>& val) { + ::testing::AssertionResult message = ::testing::AssertionFailure(); + bool passed = true; + if (ref.size() != val.size()) { + message << "Expected " << ref.size() << " rects, got " << val.size() << "."; + passed = false; + } + for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) { + ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]); + if (rectResult == false) { + message << "First different rect at " << rectIndex << ": " << rectResult.message(); + passed = false; + break; + } + } + + if (passed) { + return ::testing::AssertionSuccess(); + } else { + message << "\nReference:"; + for (auto state = ref.begin(); state != ref.end(); ++state) { + message << "\n" << *state; + } + message << "\nActual:"; + for (auto state = val.begin(); state != val.end(); ++state) { + message << "\n" << *state; + } + } + return message; +} + +void startSurfaceFlinger() { + ALOGI("Start SurfaceFlinger"); + system("start surfaceflinger"); + + sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<android::IBinder> sf; + while (sf == nullptr) { + std::this_thread::sleep_for(10ms); + sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); + } + ALOGV("SurfaceFlinger running"); +} + +void stopSurfaceFlinger() { + ALOGI("Stop SurfaceFlinger"); + system("stop surfaceflinger"); + sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<android::IBinder> sf; + while (sf != nullptr) { + std::this_thread::sleep_for(10ms); + sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); + } + ALOGV("SurfaceFlinger stopped"); +} + +//////////////////////////////////////////////// + +void FakeHwcEnvironment::SetUp() { + ALOGI("Test env setup"); + system("setenforce 0"); + system("stop"); + property_set("debug.sf.nobootanimation", "1"); + { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.nobootanimation", value, "0"); + LOG_FATAL_IF(atoi(value) != 1, "boot skip not set"); + } + // TODO: Try registering the mock as the default service instead. + property_set("debug.sf.hwc_service_name", "mock"); + // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files. + property_set("debug.sf.treble_testing_override", "true"); +} + +void FakeHwcEnvironment::TearDown() { + ALOGI("Test env tear down"); + system("stop"); + // Wait for mock call signaling teardown? + property_set("debug.sf.nobootanimation", "0"); + property_set("debug.sf.hwc_service_name", "default"); + ALOGI("Test env tear down - done"); +} + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h new file mode 100644 index 0000000000..74dc0e51bb --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h @@ -0,0 +1,119 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "FakeComposerClient.h" + +#include <gui/SurfaceComposerClient.h> + +#include <hardware/hwcomposer_defs.h> + +#include <log/log.h> + +#include <gtest/gtest.h> + +// clang-format off +// Note: This needs to reside in the global namespace for the GTest to use it +inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) { + return os << "(" << rect.left << "," + << rect.top << "," + << rect.right << "," + << rect.bottom << ")"; +} + +inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) { + return os << "(" << rect.left << "," + << rect.top << "," + << rect.right << "," + << rect.bottom << ")"; +} +// clang-format on + +namespace sftest { + +class RenderState; + +// clang-format off +inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) { + return a.top == b.top && + a.left == b.left && + a.bottom == b.bottom && + a.right == b.right; +} + +inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) { + return a.top == b.top && + a.left == b.left && + a.bottom == b.bottom && + a.right == b.right; +} +// clang-format on + +inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) { + return !(a == b); +} + +inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) { + return !(a == b); +} + +::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val); +::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, + const std::vector<RenderState>& val); + +void startSurfaceFlinger(); +void stopSurfaceFlinger(); + +class FakeHwcEnvironment : public ::testing::Environment { +public: + virtual ~FakeHwcEnvironment() {} + void SetUp() override; + void TearDown() override; +}; + +/* + * All surface state changes are supposed to happen inside a global + * transaction. GlobalTransactionScope object at the beginning of + * scope automates the process. The resulting scope gives a visual cue + * on the span of the transaction as well. + * + * Closing the transaction is synchronous, i.e., it waits for + * SurfaceFlinger to composite one frame. Now, the FakeComposerClient + * is built to explicitly request vsyncs one at the time. A delayed + * request must be made before closing the transaction or the test + * thread stalls until SurfaceFlinger does an emergency vsync by + * itself. GlobalTransactionScope encapsulates this vsync magic. + */ +class GlobalTransactionScope { +public: + GlobalTransactionScope(FakeComposerClient& composer) : mComposer(composer) { + android::SurfaceComposerClient::openGlobalTransaction(); + } + ~GlobalTransactionScope() { + int frameCount = mComposer.getFrameCount(); + mComposer.runVSyncAfter(1ms); + android::SurfaceComposerClient::closeGlobalTransaction(true); + // Make sure that exactly one frame has been rendered. + mComposer.waitUntilFrame(frameCount + 1); + LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(), + "Unexpected frame advance. Delta: %d", + mComposer.getFrameCount() - frameCount); + } + FakeComposerClient& mComposer; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h new file mode 100644 index 0000000000..0059289d4f --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/RenderState.h @@ -0,0 +1,44 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hardware/hwcomposer2.h> + +#include <vector> + +namespace sftest { +// Description of a rendered rectangle. Should only contain +// instructions necessary to rasterize the rectangle. The full scene +// is given as a sorted list of rectangles, bottom layer at index 0. +class RenderState { +public: + RenderState() = default; + // Default copy-ctor + + hwc_rect_t mDisplayFrame = {0, 0, 0, 0}; + hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f}; + std::vector<hwc_rect_t> mVisibleRegion; + hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE; + buffer_handle_t mBuffer = 0; + uint32_t mSwapCount = 0; // How many set buffer calls to the layer. + int32_t mAcquireFence = 0; // Probably should not be here. + float mPlaneAlpha = 0.f; + hwc_color_t mLayerColor = {0, 0, 0, 0}; + hwc_transform_t mTransform = static_cast<hwc_transform_t>(0); +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp new file mode 100644 index 0000000000..8902ede301 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -0,0 +1,1306 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeHwcTest" + +#include "FakeComposerClient.h" +#include "FakeComposerService.h" +#include "FakeComposerUtils.h" + +#include <gui/ISurfaceComposer.h> +#include <gui/LayerDebugInfo.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> + +#include <private/gui/ComposerService.h> +#include <private/gui/LayerState.h> + +#include <ui/DisplayInfo.h> + +#include <android/native_window.h> + +#include <android/hidl/manager/1.0/IServiceManager.h> + +#include <hwbinder/ProcessState.h> + +#include <binder/ProcessState.h> + +#include <log/log.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <limits> + +using namespace std::chrono_literals; + +using namespace android; +using namespace android::hardware; + +using namespace sftest; + +namespace { + +// Mock test helpers +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::_; + +/////////////////////////////////////////////// + +struct TestColor { +public: + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; + +constexpr static TestColor RED = {195, 63, 63, 255}; +constexpr static TestColor LIGHT_RED = {255, 177, 177, 255}; +constexpr static TestColor GREEN = {63, 195, 63, 255}; +constexpr static TestColor BLUE = {63, 63, 195, 255}; +constexpr static TestColor DARK_GRAY = {63, 63, 63, 255}; +constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255}; + +// Fill an RGBA_8888 formatted surface with a single color. +static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color, + bool unlock = true) { + ANativeWindow_Buffer outBuffer; + sp<Surface> s = sc->getSurface(); + ASSERT_TRUE(s != nullptr); + ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr)); + uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits); + for (int y = 0; y < outBuffer.height; y++) { + for (int x = 0; x < outBuffer.width; x++) { + uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); + pixel[0] = color.r; + pixel[1] = color.g; + pixel[2] = color.b; + pixel[3] = color.a; + } + } + if (unlock) { + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); + } +} + +inline RenderState makeSimpleRect(int left, int top, int right, int bottom) { + RenderState res; + res.mDisplayFrame = hwc_rect_t{left, top, right, bottom}; + res.mPlaneAlpha = 1.0f; + res.mSwapCount = 0; + res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left), + static_cast<float>(bottom - top)}; + return res; +} + +inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right, + unsigned int bottom) { + EXPECT_LE(left, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(top, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(right, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX)); + return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right), + static_cast<int>(bottom)); +} + +//////////////////////////////////////////////// + +class DisplayTest : public ::testing::Test { +public: + class MockComposerClient : public FakeComposerClient { + public: + MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType)); + MOCK_METHOD4(getDisplayAttribute, + Error(Display display, Config config, IComposerClient::Attribute attribute, + int32_t* outValue)); + + // Re-routing to basic fake implementation + Error getDisplayAttributeFake(Display display, Config config, + IComposerClient::Attribute attribute, int32_t* outValue) { + return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue); + } + }; + +protected: + void SetUp() override; + void TearDown() override; + + sp<IComposer> mFakeService; + sp<SurfaceComposerClient> mComposerClient; + + MockComposerClient* mMockComposer; +}; + +void DisplayTest::SetUp() { + // TODO: The mMockComposer should be a unique_ptr, but it needs to + // outlive the test class. Currently ComposerClient only dies + // when the service is replaced. The Mock deletes itself when + // removeClient is called on it, which is ugly. This can be + // changed if HIDL ServiceManager allows removing services or + // ComposerClient starts taking the ownership of the contained + // implementation class. Moving the fake class to the HWC2 + // interface instead of the current Composer interface might also + // change the situation. + mMockComposer = new MockComposerClient; + sp<ComposerClient> client = new ComposerClient(*mMockComposer); + mMockComposer->setClient(client.get()); + mFakeService = new FakeComposerService(client); + mFakeService->registerAsService("mock"); + + android::hardware::ProcessState::self()->startThreadPool(); + android::ProcessState::self()->startThreadPool(); + + EXPECT_CALL(*mMockComposer, getDisplayType(1, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), + Return(Error::NONE))); + // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy + // but encoding that here exactly. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _)) + .Times(5) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + // TODO: Find out what code is generating the ID 0. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _)) + .Times(5) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + + startSurfaceFlinger(); + + // Fake composer wants to enable VSync injection + mMockComposer->onSurfaceFlingerStart(); + + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); +} + +void DisplayTest::TearDown() { + mComposerClient->dispose(); + mComposerClient = nullptr; + + // Fake composer needs to release SurfaceComposerClient before the stop. + mMockComposer->onSurfaceFlingerStop(); + stopSurfaceFlinger(); + + mFakeService = nullptr; + // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime + // management. + mMockComposer = nullptr; +} + +TEST_F(DisplayTest, Hotplug) { + ALOGD("DisplayTest::Hotplug"); + + EXPECT_CALL(*mMockComposer, getDisplayType(2, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), + Return(Error::NONE))); + // The attribute queries will get done twice. This is for defaults + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _)) + .Times(2 * 3) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + // ... and then special handling for dimensions. Specifying this + // rules later means that gmock will try them first, i.e., + // ordering of width/height vs. the default implementation for + // other queries is significant. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE))); + + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE))); + + // TODO: Width and height queries are not actually called. Display + // info returns dimensions 0x0 in display info. Why? + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::CONNECTED); + + { + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ASSERT_EQ(400u, info.w); + ASSERT_EQ(200u, info.h); + + auto surfaceControl = + mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(surfaceControl != nullptr); + ASSERT_TRUE(surfaceControl->isValid()); + fillSurfaceRGBA8(surfaceControl, BLUE); + + { + GlobalTransactionScope gts(*mMockComposer); + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, surfaceControl->show()); + } + } + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::DISCONNECTED); + + mMockComposer->clearFrames(); + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::CONNECTED); + + { + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ASSERT_EQ(400u, info.w); + ASSERT_EQ(200u, info.h); + + auto surfaceControl = + mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(surfaceControl != nullptr); + ASSERT_TRUE(surfaceControl->isValid()); + fillSurfaceRGBA8(surfaceControl, BLUE); + + { + GlobalTransactionScope gts(*mMockComposer); + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, surfaceControl->show()); + } + } + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::DISCONNECTED); +} + +//////////////////////////////////////////////// + +class TransactionTest : public ::testing::Test { +protected: + // Layer array indexing constants. + constexpr static int BG_LAYER = 0; + constexpr static int FG_LAYER = 1; + + static void SetUpTestCase(); + static void TearDownTestCase(); + + void SetUp() override; + void TearDown() override; + + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mBGSurfaceControl; + sp<SurfaceControl> mFGSurfaceControl; + std::vector<RenderState> mBaseFrame; + uint32_t mDisplayWidth; + uint32_t mDisplayHeight; + + static FakeComposerClient* sFakeComposer; +}; + +FakeComposerClient* TransactionTest::sFakeComposer; + +void TransactionTest::SetUpTestCase() { + // TODO: See TODO comment at DisplayTest::SetUp for background on + // the lifetime of the FakeComposerClient. + sFakeComposer = new FakeComposerClient; + sp<ComposerClient> client = new ComposerClient(*sFakeComposer); + sFakeComposer->setClient(client.get()); + sp<IComposer> fakeService = new FakeComposerService(client); + fakeService->registerAsService("mock"); + + android::hardware::ProcessState::self()->startThreadPool(); + android::ProcessState::self()->startThreadPool(); + + startSurfaceFlinger(); + + // Fake composer wants to enable VSync injection + sFakeComposer->onSurfaceFlingerStart(); +} + +void TransactionTest::TearDownTestCase() { + // Fake composer needs to release SurfaceComposerClient before the stop. + sFakeComposer->onSurfaceFlingerStop(); + stopSurfaceFlinger(); + // TODO: This is deleted when the ComposerClient calls + // removeClient. Devise better lifetime control. + sFakeComposer = nullptr; +} + +void TransactionTest::SetUp() { + ALOGI("TransactionTest::SetUp"); + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + ALOGI("TransactionTest::SetUp - display"); + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + + mDisplayWidth = info.w; + mDisplayHeight = info.h; + + // Background surface + mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth, + mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mBGSurfaceControl != nullptr); + ASSERT_TRUE(mBGSurfaceControl->isValid()); + fillSurfaceRGBA8(mBGSurfaceControl, BLUE); + + // Foreground surface + mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mFGSurfaceControl != nullptr); + ASSERT_TRUE(mFGSurfaceControl->isValid()); + + fillSurfaceRGBA8(mFGSurfaceControl, RED); + + SurfaceComposerClient::openGlobalTransaction(); + + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show()); + + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX - 1)); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64)); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show()); + + // Synchronous transaction will stop this thread, so we set up a + // delayed, off-thread vsync request before closing the + // transaction. In the test code this is usually done with + // GlobalTransactionScope. Leaving here in the 'vanilla' form for + // reference. + ASSERT_EQ(0, sFakeComposer->getFrameCount()); + sFakeComposer->runVSyncAfter(1ms); + SurfaceComposerClient::closeGlobalTransaction(true); + sFakeComposer->waitUntilFrame(1); + + // Reference data. This is what the HWC should see. + static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing"); + mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight)); + mBaseFrame[BG_LAYER].mSwapCount = 1; + mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64)); + mBaseFrame[FG_LAYER].mSwapCount = 1; + + auto frame = sFakeComposer->getFrameRects(0); + ASSERT_TRUE(framesAreSame(mBaseFrame, frame)); +} + +void TransactionTest::TearDown() { + ALOGD("TransactionTest::TearDown"); + + mComposerClient->dispose(); + mBGSurfaceControl = 0; + mFGSurfaceControl = 0; + mComposerClient = 0; + + sFakeComposer->runVSyncAndWait(); + mBaseFrame.clear(); + sFakeComposer->clearFrames(); + ASSERT_EQ(0, sFakeComposer->getFrameCount()); + + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + std::vector<LayerDebugInfo> layers; + status_t result = sf->getLayerDebugInfo(&layers); + if (result != NO_ERROR) { + ALOGE("Failed to get layers %s %d", strerror(-result), result); + } else { + // If this fails, the test being torn down leaked layers. + EXPECT_EQ(0u, layers.size()); + if (layers.size() > 0) { + for (auto layer = layers.begin(); layer != layers.end(); ++layer) { + std::cout << to_string(*layer).c_str(); + } + // To ensure the next test has clean slate, will run the class + // tear down and setup here. + TearDownTestCase(); + SetUpTestCase(); + } + } + ALOGD("TransactionTest::TearDown - complete"); +} + +TEST_F(TransactionTest, LayerMove) { + ALOGD("TransactionTest::LayerMove"); + + // The scope opens and closes a global transaction and, at the + // same time, makes sure the SurfaceFlinger progresses one frame + // after the transaction closes. The results of the transaction + // should be available in the latest frame stored by the fake + // composer. + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128)); + // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls. + // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?) + // + // sFakeComposer->runVSyncAndWait(); + } + + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + + ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's + // no extra frames. + + // NOTE: Frame 0 is produced in the SetUp. + auto frame1Ref = mBaseFrame; + frame1Ref[FG_LAYER].mDisplayFrame = + hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves. + EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1))); + + auto frame2Ref = frame1Ref; + frame2Ref[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2))); +} + +TEST_F(TransactionTest, LayerResize) { + ALOGD("TransactionTest::LayerResize"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128)); + } + + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + + ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's + // no extra frames. + + auto frame1Ref = mBaseFrame; + // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted. + EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1))); + + auto frame2Ref = frame1Ref; + frame2Ref[FG_LAYER].mSwapCount++; + frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128}; + frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f}; + EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2))); +} + +TEST_F(TransactionTest, LayerCrop) { + // TODO: Add scaling to confirm that crop happens in buffer space? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(16, 16, 32, 32); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setCrop(cropRect)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f}; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerFinalCrop) { + // TODO: Add scaling to confirm that crop happens in display space? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(32, 32, 32 + 64, 32 + 64); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // In display space we are cropping with [32, 32, 96, 96] against display rect + // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96] + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f}; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32}; + + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerFinalCropEmpty) { + // TODO: Add scaling to confirm that crop happens in display space? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(16, 16, 32, 32); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // In display space we are cropping with [16, 16, 32, 32] against display rect + // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited. + std::vector<RenderState> referenceFrame(1); + referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetLayer) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // The layers will switch order, but both are rendered because the background layer is + // transparent (RGBA8888). + std::vector<RenderState> referenceFrame(2); + referenceFrame[0] = mBaseFrame[FG_LAYER]; + referenceFrame[1] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetLayerOpaque) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3)); + ASSERT_EQ(NO_ERROR, + mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque, + layer_state_t::eLayerOpaque)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // The former foreground layer is now covered with opaque layer - it should have disappeared + std::vector<RenderState> referenceFrame(1); + referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, SetLayerStack) { + ALOGD("TransactionTest::SetLayerStack"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayerStack(1)); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerShowHide) { + ALOGD("TransactionTest::LayerShowHide"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->hide()); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show()); + } + + // Foreground layer should be back + ASSERT_EQ(3, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetAlpha) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75f)); + } + + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetFlags) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, + mFGSurfaceControl->setFlags(layer_state_t::eLayerHidden, + layer_state_t::eLayerHidden)); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetMatrix) { + struct matrixTestData { + float matrix[4]; + hwc_transform_t expectedTransform; + hwc_rect_t expectedDisplayFrame; + }; + + // The matrix operates on the display frame and is applied before + // the position is added. So, the foreground layer rect is (0, 0, + // 64, 64) is first transformed, potentially yielding negative + // coordinates and then the position (64, 64) is added yielding + // the final on-screen rectangles given. + + const matrixTestData MATRIX_TESTS[7] = // clang-format off + {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}}, + {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}}, + {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}}, + {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}}, + {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}}, + {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}}, + {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}}; + // clang-format on + constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData); + + for (int i = 0; i < TEST_COUNT; i++) { + // TODO: How to leverage the HWC2 stringifiers? + const matrixTestData& xform = MATRIX_TESTS[i]; + SCOPED_TRACE(i); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, + mFGSurfaceControl->setMatrix(xform.matrix[0], xform.matrix[1], + xform.matrix[2], xform.matrix[3])); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mTransform = xform.expectedTransform; + referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame; + + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + } +} + +#if 0 +TEST_F(TransactionTest, LayerSetMatrix2) { + { + GlobalTransactionScope gts(*sFakeComposer); + // TODO: PLEASE SPEC THE FUNCTION! + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setMatrix(0.11f, 0.123f, + -2.33f, 0.22f)); + } + auto referenceFrame = mBaseFrame; + // TODO: Is this correct for sure? + //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90; + + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} +#endif + +TEST_F(TransactionTest, DeferredTransaction) { + // Synchronization surface + constexpr static int SYNC_LAYER = 2; + auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(syncSurfaceControl != nullptr); + ASSERT_TRUE(syncSurfaceControl->isValid()); + + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->setLayer(INT32_MAX - 1)); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->setPosition(mDisplayWidth - 2, mDisplayHeight - 2)); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->show()); + } + auto referenceFrame = mBaseFrame; + referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2, + mDisplayWidth - 1, mDisplayHeight - 1)); + referenceFrame[SYNC_LAYER].mSwapCount = 1; + EXPECT_EQ(2, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // set up two deferred transactions on different frames - these should not yield composited + // frames + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75)); + mFGSurfaceControl + ->deferTransactionUntil(syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber()); + } + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128)); + mFGSurfaceControl + ->deferTransactionUntil(syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber() + 1); + } + EXPECT_EQ(4, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // should trigger the first deferred transaction, but not the second one + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + sFakeComposer->runVSyncAndWait(); + EXPECT_EQ(5, sFakeComposer->getFrameCount()); + + referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f; + referenceFrame[SYNC_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // should show up immediately since it's not deferred + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0)); + } + referenceFrame[FG_LAYER].mPlaneAlpha = 1.f; + EXPECT_EQ(6, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // trigger the second deferred transaction + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + sFakeComposer->runVSyncAndWait(); + // TODO: Compute from layer size? + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64}; + referenceFrame[SYNC_LAYER].mSwapCount++; + EXPECT_EQ(7, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, SetRelativeLayer) { + constexpr int RELATIVE_LAYER = 2; + auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64, + PIXEL_FORMAT_RGBA_8888, 0); + fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED); + + // Now we stack the surface above the foreground surface and make sure it is visible. + { + GlobalTransactionScope gts(*sFakeComposer); + relativeSurfaceControl->setPosition(64, 64); + relativeSurfaceControl->show(); + relativeSurfaceControl->setRelativeLayer(mFGSurfaceControl->getHandle(), 1); + } + auto referenceFrame = mBaseFrame; + // NOTE: All three layers will be visible as the surfaces are + // transparent because of the RGBA format. + referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64)); + referenceFrame[RELATIVE_LAYER].mSwapCount = 1; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // A call to setLayer will override a call to setRelativeLayer + { + GlobalTransactionScope gts(*sFakeComposer); + relativeSurfaceControl->setLayer(0); + } + + // Previous top layer will now appear at the bottom. + auto referenceFrame2 = mBaseFrame; + referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]); + EXPECT_EQ(3, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +class ChildLayerTest : public TransactionTest { +protected: + constexpr static int CHILD_LAYER = 2; + + void SetUp() override { + TransactionTest::SetUp(); + mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + fillSurfaceRGBA8(mChild, LIGHT_GRAY); + + sFakeComposer->runVSyncAndWait(); + mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10)); + mBaseFrame[CHILD_LAYER].mSwapCount = 1; + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + } + void TearDown() override { + mChild = 0; + TransactionTest::TearDown(); + } + + sp<SurfaceControl> mChild; +}; + +TEST_F(ChildLayerTest, Positioning) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + // Move to the same position as in the original setup. + mFGSurfaceControl->setPosition(64, 64); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0)); + } + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Cropping) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + mFGSurfaceControl->setCrop(Rect(0, 0, 5, 5)); + } + // NOTE: The foreground surface would be occluded by the child + // now, but is included in the stack because the child is + // transparent. + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, FinalCropping) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + mFGSurfaceControl->setFinalCrop(Rect(0, 0, 5, 5)); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Constraints) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mFGSurfaceControl->setPosition(0, 0); + mChild->setPosition(63, 63); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Scaling) { + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setPosition(0, 0); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0); + } + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, LayerAlpha) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + ASSERT_EQ(NO_ERROR, mChild->setAlpha(0.5)); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.5)); + } + + auto referenceFrame2 = referenceFrame; + referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f; + referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, ReparentChildren) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + mFGSurfaceControl->setPosition(64, 64); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle()); + } + + auto referenceFrame2 = referenceFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, DetachChildren) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + mFGSurfaceControl->setPosition(64, 64); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->detachChildren(); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->hide(); + } + + // Nothing should have changed. The child control becomes a no-op + // zombie on detach. See comments for detachChildren in the + // SurfaceControl.h file. + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setOverrideScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // We cause scaling by 2. + mFGSurfaceControl->setSize(128, 128); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +// Regression test for b/37673612 +TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + } + + // We set things up as in b/37673612 so that there is a mismatch between the buffer size and + // the WM specified state size. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 64); + } + + sp<Surface> s = mFGSurfaceControl->getSurface(); + auto anw = static_cast<ANativeWindow*>(s.get()); + native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); + native_window_set_buffers_dimensions(anw, 64, 128); + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + + // The child should still be in the same place and not have any strange scaling as in + // b/37673612. + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f}; + referenceFrame[FG_LAYER].mSwapCount++; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Bug36858924) { + // Destroy the child layer + mChild.clear(); + + // Now recreate it as hidden + mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden, + mFGSurfaceControl.get()); + + // Show the child layer in a deferred transaction + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->deferTransactionUntil(mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()); + mChild->show(); + } + + // Render the foreground surface a few times + // + // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third + // frame because SurfaceFlinger would never process the deferred transaction and would therefore + // never acquire/release the first buffer + ALOGI("Filling 1"); + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 2"); + fillSurfaceRGBA8(mFGSurfaceControl, BLUE); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 3"); + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 4"); + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); +} + +class LatchingTest : public TransactionTest { +protected: + void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); } + + void unlockFGBuffer() { + sp<Surface> s = mFGSurfaceControl->getSurface(); + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); + sFakeComposer->runVSyncAndWait(); + } + + void completeFGResize() { + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + } + void restoreInitialState() { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(64, 64); + mFGSurfaceControl->setPosition(64, 64); + mFGSurfaceControl->setCrop(Rect(0, 0, 64, 64)); + mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1)); + } +}; + +TEST_F(LatchingTest, SurfacePositionLatching) { + // By default position can be updated even while + // a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(32, 32); + mFGSurfaceControl->setPosition(100, 100); + } + + // The size should not have updated as we have not provided a new buffer. + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + // Now we repeat with setGeometryAppliesWithResize + // and verify the position DOESN'T latch. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setSize(32, 32); + mFGSurfaceControl->setPosition(100, 100); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32}; + referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f}; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(LatchingTest, CropLatching) { + // Normally the crop applies immediately even while a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63)); + } + + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63}; + referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63}; + referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f}; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(LatchingTest, FinalCropLatching) { + // Normally the crop applies immediately even while a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; + referenceFrame1[FG_LAYER].mSourceCrop = + hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; + referenceFrame2[FG_LAYER].mSourceCrop = + hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +// In this test we ensure that setGeometryAppliesWithResize actually demands +// a buffer of the new size, and not just any size. +TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) { + // Normally the crop applies immediately even while a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; + referenceFrame1[FG_LAYER].mSourceCrop = + hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + // In order to prepare to submit a buffer at the wrong size, we acquire it prior to + // initiating the resize. + lockAndFillFGBuffer(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + // We now submit our old buffer, at the old size, and ensure it doesn't + // trigger geometry latching. + unlockFGBuffer(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); + + completeFGResize(); + auto referenceFrame3 = referenceFrame2; + referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; + referenceFrame3[FG_LAYER].mSourceCrop = + hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; + referenceFrame3[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame())); +} + +TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) { + // In this scenario, we attempt to set the final crop a second time while the resize + // is still pending, and ensure we are successful. Success meaning the second crop + // is the one which eventually latches and not the first. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +} // namespace + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment; + ::testing::AddGlobalTestEnvironment(fakeEnvironment); + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp index 062485ea52..4055527b13 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp +++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp @@ -382,7 +382,9 @@ public: if (outErr) { *outErr = err; } else { - ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set cursor position"; + ASSERT_TRUE((err == HWC2_ERROR_NONE) || + (err == HWC2_ERROR_BAD_LAYER)) << + "failed to set cursor position"; } } @@ -652,7 +654,7 @@ public: hwc2_layer_request_t request = requests.at(i); EXPECT_EQ(std::count(layers.begin(), layers.end(), requestedLayer), - 0) << "get display requests returned an unknown layer"; + 1) << "get display requests returned an unknown layer"; EXPECT_NE(request, 0) << "returned empty request for layer " << requestedLayer; @@ -1603,9 +1605,10 @@ protected: EXPECT_EQ(layers.size(), fences.size()); for (int32_t fence : fences) { - EXPECT_GE(sync_wait(fence, msWait), 0); - if (fence >= 0) + if (fence >= 0) { + EXPECT_GE(sync_wait(fence, msWait), 0); close(fence); + } } } @@ -1643,8 +1646,9 @@ protected: testLayers->getBlendMode(layer))); EXPECT_NO_FATAL_FAILURE(setLayerColor(display, layer, testLayers->getColor(layer))); - EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, cursor.left, - cursor.top)); + if (composition == HWC2_COMPOSITION_CURSOR) + EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, + cursor.left, cursor.top)); EXPECT_NO_FATAL_FAILURE(setLayerDataspace(display, layer, testLayers->getDataspace(layer))); EXPECT_NO_FATAL_FAILURE(setLayerDisplayFrame(display, layer, @@ -2895,7 +2899,6 @@ TEST_F(Hwc2Test, SET_CURSOR_POSITION_composition_type_unset) ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete, [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer, Hwc2TestLayer* testLayer, hwc2_error_t* outErr) { - const hwc_rect_t cursorPosition = testLayer->getCursorPosition(); EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer, cursorPosition.left, cursorPosition.top, outErr)); @@ -4406,11 +4409,11 @@ TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_display) /* TESTCASE: Tests that the HWC2 cannot destroy a physical display. */ TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_parameter) { - hwc2_display_t display = HWC_DISPLAY_PRIMARY; hwc2_error_t err = HWC2_ERROR_NONE; - - ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err)); - EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code"; + for (auto display : mDisplays) { + ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err)); + EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code"; + } } /* TESTCASE: Tests that the HWC2 can get the max virtual display count. */ diff --git a/services/thermalservice/Android.bp b/services/thermalservice/Android.bp new file mode 100644 index 0000000000..d754560ea5 --- /dev/null +++ b/services/thermalservice/Android.bp @@ -0,0 +1,61 @@ +subdirs = [ + "libthermalcallback" +] + +cc_library { + name: "libthermalservice", + + srcs: [ + "aidl/android/os/IThermalEventListener.aidl", + "aidl/android/os/IThermalService.aidl", + "aidl/android/os/Temperature.cpp", + ], + aidl: { + include_dirs: ["frameworks/native/services/thermalservice/aidl"], + export_aidl_headers: true, + }, + export_include_dirs: ["aidl"], + + shared_libs: [ + "libbinder", + "libutils", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], +} + +cc_binary { + name: "thermalserviced", + + srcs: [ + "ThermalService.cpp", + "thermalserviced.cpp", + ], + + include_dirs: ["frameworks/native"], + + shared_libs: [ + "libthermalservice", + "libbinder", + "libutils", + "libthermalcallback", + "android.hardware.thermal@1.1", + "libhidlbase", + "libhidltransport", + "liblog", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], + + init_rc: ["thermalservice.rc"], +} diff --git a/services/thermalservice/ThermalService.cpp b/services/thermalservice/ThermalService.cpp new file mode 100644 index 0000000000..6e09a83872 --- /dev/null +++ b/services/thermalservice/ThermalService.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ThermalService.h" +#include <android/os/IThermalService.h> +#include <android/os/IThermalEventListener.h> +#include <android/os/Temperature.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <utils/Errors.h> +#include <utils/Mutex.h> +#include <utils/String16.h> + +namespace android { +namespace os { + +/** + * Notify registered listeners of a thermal throttling start/stop event. + * @param temperature the temperature at which the event was generated + */ +binder::Status ThermalService::notifyThrottling( + const bool isThrottling, const Temperature& temperature) { + Mutex::Autolock _l(mListenersLock); + + mThrottled = isThrottling; + mThrottleTemperature = temperature; + + for (size_t i = 0; i < mListeners.size(); i++) { + mListeners[i]->notifyThrottling(isThrottling, temperature); + } + return binder::Status::ok(); +} + +/** + * Query whether the system is currently thermal throttling. + * @return true if currently thermal throttling, else false + */ +binder::Status ThermalService::isThrottling(bool* _aidl_return) { + Mutex::Autolock _l(mListenersLock); + *_aidl_return = mThrottled; + return binder::Status::ok(); +} + +/** + * Register a new thermal event listener. + * @param listener the client's IThermalEventListener instance to which + * notifications are to be sent + */ +binder::Status ThermalService::registerThermalEventListener( + const sp<IThermalEventListener>& listener) { + { + if (listener == NULL) + return binder::Status::ok(); + Mutex::Autolock _l(mListenersLock); + // check whether this is a duplicate + for (size_t i = 0; i < mListeners.size(); i++) { + if (IInterface::asBinder(mListeners[i]) == + IInterface::asBinder(listener)) { + return binder::Status::ok(); + } + } + + mListeners.add(listener); + IInterface::asBinder(listener)->linkToDeath(this); + } + + return binder::Status::ok(); +} + +/** + * Unregister a previously-registered thermal event listener. + * @param listener the client's IThermalEventListener instance to which + * notifications are to no longer be sent + */ +binder::Status ThermalService::unregisterThermalEventListener( + const sp<IThermalEventListener>& listener) { + if (listener == NULL) + return binder::Status::ok(); + Mutex::Autolock _l(mListenersLock); + for (size_t i = 0; i < mListeners.size(); i++) { + if (IInterface::asBinder(mListeners[i]) == + IInterface::asBinder(listener)) { + IInterface::asBinder(mListeners[i])->unlinkToDeath(this); + mListeners.removeAt(i); + break; + } + } + + return binder::Status::ok(); +} + +void ThermalService::binderDied(const wp<IBinder>& who) { + Mutex::Autolock _l(mListenersLock); + + for (size_t i = 0; i < mListeners.size(); i++) { + if (IInterface::asBinder(mListeners[i]) == who) { + mListeners.removeAt(i); + break; + } + } +} + +/** + * Publish the supplied ThermalService to servicemanager. + */ +void ThermalService::publish( + const sp<ThermalService>& service) { + defaultServiceManager()->addService(String16("thermalservice"), + service); +} + +} // namespace os +} // namespace android diff --git a/services/thermalservice/ThermalService.h b/services/thermalservice/ThermalService.h new file mode 100644 index 0000000000..17dfcbcd37 --- /dev/null +++ b/services/thermalservice/ThermalService.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_THERMALSERVICE_THERMALSERVICE_H +#define ANDROID_THERMALSERVICE_THERMALSERVICE_H + +#include <android/os/BnThermalService.h> +#include <android/os/IThermalEventListener.h> +#include <android/os/Temperature.h> +#include <utils/Mutex.h> +#include <utils/String16.h> +#include <utils/Vector.h> + +namespace android { +namespace os { + +class ThermalService : public BnThermalService, + public IBinder::DeathRecipient { +public: + ThermalService() : mThrottled(false) {}; + void publish(const sp<ThermalService>& service); + binder::Status notifyThrottling( + const bool isThrottling, const Temperature& temperature); + +private: + Mutex mListenersLock; + Vector<sp<IThermalEventListener> > mListeners; + bool mThrottled; + Temperature mThrottleTemperature; + + binder::Status registerThermalEventListener( + const sp<IThermalEventListener>& listener); + binder::Status unregisterThermalEventListener( + const sp<IThermalEventListener>& listener); + binder::Status isThrottling(bool* _aidl_return); + void binderDied(const wp<IBinder>& who); +}; + +}; // namespace os +}; // namespace android + +#endif // ANDROID_THERMALSERVICE_THERMALSERVICE_H diff --git a/services/thermalservice/aidl/android/os/IThermalEventListener.aidl b/services/thermalservice/aidl/android/os/IThermalEventListener.aidl new file mode 100644 index 0000000000..050325e2fc --- /dev/null +++ b/services/thermalservice/aidl/android/os/IThermalEventListener.aidl @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.Temperature; + +/** + * Listener for thermal events. + * {@hide} + */ +oneway interface IThermalEventListener { + /** + * Called when a thermal throttling start/stop event is received. + * @param temperature the temperature at which the event was generated. + */ + void notifyThrottling( + in boolean isThrottling, in Temperature temperature); +} diff --git a/services/thermalservice/aidl/android/os/IThermalService.aidl b/services/thermalservice/aidl/android/os/IThermalService.aidl new file mode 100644 index 0000000000..e699202e64 --- /dev/null +++ b/services/thermalservice/aidl/android/os/IThermalService.aidl @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.IThermalEventListener; +import android.os.Temperature; + +/** {@hide} */ +interface IThermalService { + /** + * Register a listener for thermal events. + * @param listener the IThermalEventListener to be notified. + * {@hide} + */ + void registerThermalEventListener(in IThermalEventListener listener); + /** + * Unregister a previously-registered listener for thermal events. + * @param listener the IThermalEventListener to no longer be notified. + * {@hide} + */ + void unregisterThermalEventListener(in IThermalEventListener listener); + /** + * Send a thermal throttling start/stop notification to all listeners. + * @param temperature the temperature at which the event was generated. + * {@hide} + */ + oneway void notifyThrottling( + in boolean isThrottling, in Temperature temperature); + /** + * Return whether system performance is currently thermal throttling. + * {@hide} + */ + boolean isThrottling(); +} diff --git a/services/thermalservice/aidl/android/os/Temperature.aidl b/services/thermalservice/aidl/android/os/Temperature.aidl new file mode 100644 index 0000000000..0293c39fa9 --- /dev/null +++ b/services/thermalservice/aidl/android/os/Temperature.aidl @@ -0,0 +1,5 @@ +package android.os; + +/* Encodes a temperature used by ThermalService. */ + +parcelable Temperature cpp_header "android/os/Temperature.h"; diff --git a/services/thermalservice/aidl/android/os/Temperature.cpp b/services/thermalservice/aidl/android/os/Temperature.cpp new file mode 100644 index 0000000000..df207b7ac3 --- /dev/null +++ b/services/thermalservice/aidl/android/os/Temperature.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android/os/Temperature.h" + +#include <math.h> +#include <stdint.h> +#include <binder/Parcel.h> +#include <hardware/thermal.h> +#include <sys/types.h> +#include <utils/Errors.h> + +namespace android { +namespace os { + +Temperature::Temperature() : value_(NAN), type_(DEVICE_TEMPERATURE_UNKNOWN) {} + +Temperature::Temperature(const float value, const int type) : + value_(value), type_(type) {} + +Temperature::~Temperature() {} + +/* + * Parcel read/write code must be kept in sync with + * frameworks/base/core/java/android/os/Temperature.java + */ + +status_t Temperature::readFromParcel(const Parcel* p) { + value_ = p->readFloat(); + type_ = p->readInt32(); + return OK; +} + +status_t Temperature::writeToParcel(Parcel* p) const { + p->writeFloat(value_); + p->writeInt32(type_); + return OK; +} + +} // namespace os +} // namespace android diff --git a/services/thermalservice/aidl/android/os/Temperature.h b/services/thermalservice/aidl/android/os/Temperature.h new file mode 100644 index 0000000000..bbc5607f8b --- /dev/null +++ b/services/thermalservice/aidl/android/os/Temperature.h @@ -0,0 +1,33 @@ +#ifndef ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H +#define ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H + +#include <binder/Parcelable.h> + +namespace android { +namespace os { + +class Temperature : public Parcelable { + public: + + Temperature(); + Temperature(const float value, const int type); + ~Temperature() override; + + float getValue() const {return value_;}; + float getType() const {return type_;}; + + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + + private: + // The value of the temperature as a float, or NAN if unknown. + float value_; + // The type of the temperature, an enum temperature_type from + // hardware/thermal.h + int type_; +}; + +} // namespace os +} // namespace android + +#endif // ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H diff --git a/services/thermalservice/libthermalcallback/Android.bp b/services/thermalservice/libthermalcallback/Android.bp new file mode 100644 index 0000000000..e98506e47e --- /dev/null +++ b/services/thermalservice/libthermalcallback/Android.bp @@ -0,0 +1,19 @@ +cc_library_shared { + name: "libthermalcallback", + srcs: [ + "ThermalCallback.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + ], + include_dirs: ["frameworks/native"], + shared_libs: [ + "android.hardware.thermal@1.1", + "libhidlbase", + "libhidltransport", + "liblog", + "libthermalservice", + "libutils", + ], +} diff --git a/services/thermalservice/libthermalcallback/ThermalCallback.cpp b/services/thermalservice/libthermalcallback/ThermalCallback.cpp new file mode 100644 index 0000000000..5e094fa259 --- /dev/null +++ b/services/thermalservice/libthermalcallback/ThermalCallback.cpp @@ -0,0 +1,69 @@ +#define LOG_TAG "android.hardware.thermal.thermalcallback@1.1-impl" +#include <log/log.h> + +#include "ThermalCallback.h" +#include "services/thermalservice/ThermalService.h" +#include <math.h> +#include <android/os/Temperature.h> +#include <hardware/thermal.h> + +namespace android { +namespace hardware { +namespace thermal { +namespace V1_1 { +namespace implementation { + +using ::android::os::ThermalService; +using ::android::hardware::thermal::V1_0::TemperatureType; + +// Register a binder ThermalService object for sending events +void ThermalCallback::registerThermalService(sp<ThermalService> thermalService) +{ + mThermalService = thermalService; +} + +// Methods from IThermalCallback::V1_1 follow. +Return<void> ThermalCallback::notifyThrottling( + bool isThrottling, + const android::hardware::thermal::V1_0::Temperature& temperature) { + + // Convert HIDL IThermal Temperature to binder IThermalService Temperature. + if (mThermalService != nullptr) { + float value = NAN; + int type = DEVICE_TEMPERATURE_UNKNOWN; + + switch(temperature.type) { + case TemperatureType::CPU: + type = DEVICE_TEMPERATURE_CPU; + break; + case TemperatureType::GPU: + type = DEVICE_TEMPERATURE_GPU; + break; + case TemperatureType::BATTERY: + type = DEVICE_TEMPERATURE_BATTERY; + break; + case TemperatureType::SKIN: + type = DEVICE_TEMPERATURE_SKIN; + break; + case TemperatureType::UNKNOWN: + default: + type = DEVICE_TEMPERATURE_UNKNOWN; + break; + } + + value = temperature.currentValue == UNKNOWN_TEMPERATURE ? NAN : + temperature.currentValue; + + android::os::Temperature thermal_svc_temp(value, type); + mThermalService->notifyThrottling(isThrottling, thermal_svc_temp); + } else { + ALOGE("IThermalService binder service not created, drop throttling event"); + } + return Void(); +} + +} // namespace implementation +} // namespace V1_1 +} // namespace thermal +} // namespace hardware +} // namespace android diff --git a/services/thermalservice/libthermalcallback/ThermalCallback.h b/services/thermalservice/libthermalcallback/ThermalCallback.h new file mode 100644 index 0000000000..3d72c680b4 --- /dev/null +++ b/services/thermalservice/libthermalcallback/ThermalCallback.h @@ -0,0 +1,43 @@ +#ifndef ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H +#define ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H + +#include <android/hardware/thermal/1.1/IThermalCallback.h> +#include <android/hardware/thermal/1.0/types.h> +#include <android/os/Temperature.h> +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> +#include "services/thermalservice/ThermalService.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V1_1 { +namespace implementation { + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::os::ThermalService; + +class ThermalCallback : public IThermalCallback { + public: + // Register a binder ThermalService object for sending events + void registerThermalService(sp<ThermalService> thermalService); + + // Methods from IThermalCallback::V1_1 follow. + Return<void> notifyThrottling( + bool isThrottling, + const android::hardware::thermal::V1_0::Temperature& temperature) + override; + + private: + // Our registered binder ThermalService object to use for sending events + sp<android::os::ThermalService> mThermalService; +}; + +} // namespace implementation +} // namespace V1_1 +} // namespace thermal +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H diff --git a/services/thermalservice/thermalservice.rc b/services/thermalservice/thermalservice.rc new file mode 100644 index 0000000000..b9836cec80 --- /dev/null +++ b/services/thermalservice/thermalservice.rc @@ -0,0 +1,2 @@ +service thermalservice /system/bin/thermalserviced + class core diff --git a/services/thermalservice/thermalserviced.cpp b/services/thermalservice/thermalserviced.cpp new file mode 100644 index 0000000000..8e2726669f --- /dev/null +++ b/services/thermalservice/thermalserviced.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "thermalserviced" +#include <log/log.h> + +#include "thermalserviced.h" +#include "ThermalService.h" +#include "libthermalcallback/ThermalCallback.h" + +#include <android/hardware/thermal/1.1/IThermal.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <hidl/HidlTransportSupport.h> + +using namespace android; +using ::android::hardware::thermal::V1_1::IThermal; +using ::android::hardware::thermal::V1_0::Temperature; +using ::android::hardware::thermal::V1_1::IThermalCallback; +using ::android::hardware::thermal::V1_1::implementation::ThermalCallback; +using ::android::hardware::configureRpcThreadpool; +using ::android::hardware::hidl_death_recipient; +using ::android::hidl::base::V1_0::IBase; +using ::android::os::ThermalService; + +template<typename T> +using Return = hardware::Return<T>; + +namespace { + +// Our thermalserviced main object +ThermalServiceDaemon* gThermalServiceDaemon; + +// Thermal HAL client +sp<IThermal> gThermalHal = nullptr; + +// Binder death notifier informing of Thermal HAL death. +struct ThermalServiceDeathRecipient : hidl_death_recipient { + virtual void serviceDied( + uint64_t cookie __unused, const wp<IBase>& who __unused) { + gThermalHal = nullptr; + ALOGE("IThermal HAL died"); + gThermalServiceDaemon->getThermalHal(); + } +}; + +sp<ThermalServiceDeathRecipient> gThermalHalDied = nullptr; + +} // anonymous namespace + +void ThermalServiceDaemon::thermalServiceStartup() { + // Binder IThermalService startup + mThermalService = new android::os::ThermalService; + mThermalService->publish(mThermalService); + // Register IThermalService object with IThermalCallback + if (mThermalCallback != nullptr) + mThermalCallback->registerThermalService(mThermalService); + IPCThreadState::self()->joinThreadPool(); +} + +// Lookup Thermal HAL, register death notifier, register our +// ThermalCallback with the Thermal HAL. +void ThermalServiceDaemon::getThermalHal() { + gThermalHal = IThermal::getService(); + if (gThermalHal == nullptr) { + ALOGW("Unable to get Thermal HAL V1.1, vendor thermal event notification not available"); + return; + } + + // Binder death notifier for Thermal HAL + if (gThermalHalDied == nullptr) + gThermalHalDied = new ThermalServiceDeathRecipient(); + + if (gThermalHalDied != nullptr) + gThermalHal->linkToDeath(gThermalHalDied, 0x451F /* cookie */); + + if (mThermalCallback != nullptr) { + Return<void> ret = gThermalHal->registerThermalCallback( + mThermalCallback); + if (!ret.isOk()) + ALOGE("registerThermalCallback failed, status: %s", + ret.description().c_str()); + } +} + +void ThermalServiceDaemon::thermalCallbackStartup() { + // HIDL IThermalCallback startup + // Need at least 2 threads in thread pool since we wait for dead HAL + // to come back on the binder death notification thread and we need + // another thread for the incoming service now available call. + configureRpcThreadpool(2, false /* callerWillJoin */); + mThermalCallback = new ThermalCallback(); + // Lookup Thermal HAL and register our ThermalCallback. + getThermalHal(); +} + +int main(int /*argc*/, char** /*argv*/) { + gThermalServiceDaemon = new ThermalServiceDaemon(); + gThermalServiceDaemon->thermalCallbackStartup(); + gThermalServiceDaemon->thermalServiceStartup(); + /* NOTREACHED */ +} diff --git a/services/thermalservice/thermalserviced.h b/services/thermalservice/thermalserviced.h new file mode 100644 index 0000000000..309e2fe422 --- /dev/null +++ b/services/thermalservice/thermalserviced.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_THERMALSERVICE_THERMALSERVICED_H +#define ANDROID_THERMALSERVICE_THERMALSERVICED_H + +#include "ThermalService.h" +#include "libthermalcallback/ThermalCallback.h" + +using namespace android; +using ::android::hardware::thermal::V1_0::Temperature; +using ::android::hardware::thermal::V1_1::implementation::ThermalCallback; +using ::android::os::ThermalService; + +class ThermalServiceDaemon { + public: + void thermalServiceStartup(); + void thermalCallbackStartup(); + void getThermalHal(); + ThermalServiceDaemon() {}; + + private: + sp<ThermalService> mThermalService; + sp<ThermalCallback> mThermalCallback; +}; + +#endif // ANDROID_THERMALSERVICE_THERMALSERVICED_H diff --git a/services/vr/bufferhubd/Android.mk b/services/vr/bufferhubd/Android.mk index 97f0332d7a..28cf53dd82 100644 --- a/services/vr/bufferhubd/Android.mk +++ b/services/vr/bufferhubd/Android.mk @@ -22,6 +22,9 @@ sourceFiles := \ consumer_queue_channel.cpp \ producer_queue_channel.cpp \ +headerLibraries := \ + libdvr_headers + staticLibraries := \ libperformance \ libpdx_default_transport \ @@ -41,6 +44,7 @@ LOCAL_SRC_FILES := $(sourceFiles) LOCAL_CFLAGS := -DLOG_TAG=\"bufferhubd\" LOCAL_CFLAGS += -DTRACE=0 LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_GRAPHICS +LOCAL_HEADER_LIBRARIES := $(headerLibraries) LOCAL_STATIC_LIBRARIES := $(staticLibraries) LOCAL_SHARED_LIBRARIES := $(sharedLibraries) LOCAL_MODULE := bufferhubd diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp index 26843c99c8..cdb1f91795 100644 --- a/services/vr/bufferhubd/buffer_hub.cpp +++ b/services/vr/bufferhubd/buffer_hub.cpp @@ -20,8 +20,8 @@ using android::pdx::Channel; using android::pdx::ErrorStatus; using android::pdx::Message; using android::pdx::Status; -using android::pdx::rpc::DispatchRemoteMethod; using android::pdx::default_transport::Endpoint; +using android::pdx::rpc::DispatchRemoteMethod; namespace android { namespace dvr { @@ -53,7 +53,15 @@ std::string BufferHubService::DumpState(size_t /*max_length*/) { stream << " "; stream << std::setw(6) << "Format"; stream << " "; - stream << std::setw(11) << "Usage"; + stream << std::setw(10) << "Usage"; + stream << " "; + stream << std::setw(9) << "Pending"; + stream << " "; + stream << std::setw(18) << "State"; + stream << " "; + stream << std::setw(18) << "Signaled"; + stream << " "; + stream << std::setw(10) << "Index"; stream << " "; stream << "Name"; stream << std::endl; @@ -83,46 +91,15 @@ std::string BufferHubService::DumpState(size_t /*max_length*/) { stream << std::setw(8) << info.usage; stream << std::dec << std::setfill(' '); stream << " "; - stream << info.name; - stream << std::endl; - } - } - - stream << "Active Consumer Buffers:\n"; - stream << std::right; - stream << std::setw(6) << "Id"; - stream << " "; - stream << std::setw(14) << "Geometry"; - stream << " "; - stream << "Name"; - stream << std::endl; - - for (const auto& channel : channels) { - if (channel->channel_type() == BufferHubChannel::kConsumerType) { - BufferHubChannel::BufferInfo info = channel->GetBufferInfo(); - - stream << std::right; - stream << std::setw(6) << info.id; + stream << std::setw(9) << info.pending_count; stream << " "; - - if (info.consumer_count == 0) { - // consumer_count is tracked by producer. When it's zero, producer must - // have already hung up and the consumer is orphaned. - stream << std::setw(14) << "Orphaned."; - stream << (" channel_id=" + std::to_string(channel->channel_id())); - stream << std::endl; - continue; - } - - if (info.format == HAL_PIXEL_FORMAT_BLOB) { - std::string size = std::to_string(info.width) + " B"; - stream << std::setw(14) << size; - } else { - std::string dimensions = std::to_string(info.width) + "x" + - std::to_string(info.height) + "x" + - std::to_string(info.layer_count); - stream << std::setw(14) << dimensions; - } + stream << "0x" << std::hex << std::setfill('0'); + stream << std::setw(16) << info.state; + stream << " "; + stream << "0x" << std::setw(16) << info.signaled_mask; + stream << std::dec << std::setfill(' '); + stream << " "; + stream << std::setw(8) << info.index; stream << " "; stream << info.name; stream << std::endl; @@ -184,6 +161,32 @@ std::string BufferHubService::DumpState(size_t /*max_length*/) { } } + stream << std::endl; + stream << "Orphaned Consumer Buffers:\n"; + stream << std::right; + stream << std::setw(6) << "Id"; + stream << " "; + stream << std::setw(14) << "Geometry"; + stream << " "; + stream << "Name"; + stream << std::endl; + + for (const auto& channel : channels) { + BufferHubChannel::BufferInfo info = channel->GetBufferInfo(); + // consumer_count is tracked by producer. When it's zero, producer must have + // already hung up and the consumer is orphaned. + if (channel->channel_type() == BufferHubChannel::kConsumerType && + info.consumer_count == 0) { + stream << std::right; + stream << std::setw(6) << info.id; + stream << " "; + + stream << std::setw(14) << "Orphaned."; + stream << (" channel_id=" + std::to_string(channel->channel_id())); + stream << std::endl; + } + } + return stream.str(); } @@ -444,6 +447,7 @@ void BufferHubChannel::SignalAvailable() { "BufferHubChannel::SignalAvailable: channel_id=%d buffer_id=%d", channel_id(), buffer_id()); if (!IsDetached()) { + signaled_ = true; const auto status = service_->ModifyChannelEvents(channel_id_, 0, POLLIN); ALOGE_IF(!status, "BufferHubChannel::SignalAvailable: failed to signal availability " @@ -460,6 +464,7 @@ void BufferHubChannel::ClearAvailable() { "BufferHubChannel::ClearAvailable: channel_id=%d buffer_id=%d", channel_id(), buffer_id()); if (!IsDetached()) { + signaled_ = false; const auto status = service_->ModifyChannelEvents(channel_id_, POLLIN, 0); ALOGE_IF(!status, "BufferHubChannel::ClearAvailable: failed to clear availability " diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/buffer_hub.h index b0df11f2ac..270ac95114 100644 --- a/services/vr/bufferhubd/buffer_hub.h +++ b/services/vr/bufferhubd/buffer_hub.h @@ -53,6 +53,10 @@ class BufferHubChannel : public pdx::Channel { uint32_t layer_count = 0; uint32_t format = 0; uint64_t usage = 0; + size_t pending_count = 0; + uint64_t state = 0; + uint64_t signaled_mask = 0; + uint64_t index = 0; std::string name; // Data filed for producer queue. @@ -60,7 +64,9 @@ class BufferHubChannel : public pdx::Channel { UsagePolicy usage_policy{0, 0, 0, 0}; BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height, - uint32_t layer_count, uint32_t format, uint64_t usage, const std::string& name) + uint32_t layer_count, uint32_t format, uint64_t usage, + size_t pending_count, uint64_t state, uint64_t signaled_mask, + uint64_t index, const std::string& name) : id(id), type(kProducerType), consumer_count(consumer_count), @@ -69,6 +75,10 @@ class BufferHubChannel : public pdx::Channel { layer_count(layer_count), format(format), usage(usage), + pending_count(pending_count), + state(state), + signaled_mask(signaled_mask), + index(index), name(name) {} BufferInfo(int id, size_t consumer_count, size_t capacity, @@ -101,6 +111,8 @@ class BufferHubChannel : public pdx::Channel { int channel_id() const { return channel_id_; } bool IsDetached() const { return channel_id_ == kDetachedId; } + bool signaled() const { return signaled_; } + void Detach() { if (channel_type_ == kProducerType) channel_id_ = kDetachedId; @@ -124,6 +136,8 @@ class BufferHubChannel : public pdx::Channel { // buffer if it is detached and re-attached to another channel. int channel_id_; + bool signaled_; + ChannelType channel_type_; BufferHubChannel(const BufferHubChannel&) = delete; diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp index d4fc540a61..b27f218eb6 100644 --- a/services/vr/bufferhubd/bufferhubd.cpp +++ b/services/vr/bufferhubd/bufferhubd.cpp @@ -2,9 +2,10 @@ #include <unistd.h> #include <log/log.h> +#include <sys/resource.h> #include <dvr/performance_client_api.h> -#include <pdx/default_transport/service_dispatcher.h> +#include <pdx/service_dispatcher.h> #include "buffer_hub.h" @@ -16,7 +17,24 @@ int main(int, char**) { // We need to be able to create endpoints with full perms. umask(0000); - dispatcher = android::pdx::default_transport::ServiceDispatcher::Create(); + // Bump up the soft limit of open fd to the hard limit. + struct rlimit64 rlim; + ret = getrlimit64(RLIMIT_NOFILE, &rlim); + LOG_ALWAYS_FATAL_IF(ret != 0, "Failed to get nofile limit."); + + ALOGI("Current nofile limit is %llu/%llu.", rlim.rlim_cur, rlim.rlim_max); + rlim.rlim_cur = rlim.rlim_max; + ret = setrlimit64(RLIMIT_NOFILE, &rlim); + ALOGE_IF(ret < 0, "Failed to set nofile limit, error=%s", strerror(errno)); + + rlim.rlim_cur = -1; + rlim.rlim_max = -1; + if (getrlimit64(RLIMIT_NOFILE, &rlim) < 0) + ALOGE("Failed to get nofile limit."); + else + ALOGI("New nofile limit is %llu/%llu.", rlim.rlim_cur, rlim.rlim_max); + + dispatcher = android::pdx::ServiceDispatcher::Create(); CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n"); service = android::dvr::BufferHubService::Create(); diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp index ac6896ae84..a6d2dbb60c 100644 --- a/services/vr/bufferhubd/consumer_channel.cpp +++ b/services/vr/bufferhubd/consumer_channel.cpp @@ -19,9 +19,10 @@ namespace android { namespace dvr { ConsumerChannel::ConsumerChannel(BufferHubService* service, int buffer_id, - int channel_id, + int channel_id, uint64_t consumer_state_bit, const std::shared_ptr<Channel> producer) : BufferHubChannel(service, buffer_id, channel_id, kConsumerType), + consumer_state_bit_(consumer_state_bit), producer_(producer) { GetProducer()->AddConsumer(this); } @@ -32,8 +33,6 @@ ConsumerChannel::~ConsumerChannel() { channel_id(), buffer_id()); if (auto producer = GetProducer()) { - if (!released_) // Producer is waiting for our Release. - producer->OnConsumerIgnored(); producer->RemoveConsumer(this); } } @@ -43,6 +42,8 @@ BufferHubChannel::BufferInfo ConsumerChannel::GetBufferInfo() const { if (auto producer = GetProducer()) { // If producer has not hung up, copy most buffer info from the producer. info = producer->GetBufferInfo(); + } else { + info.signaled_mask = consumer_state_bit(); } info.id = buffer_id(); return info; @@ -55,6 +56,9 @@ std::shared_ptr<ProducerChannel> ConsumerChannel::GetProducer() const { void ConsumerChannel::HandleImpulse(Message& message) { ATRACE_NAME("ConsumerChannel::HandleImpulse"); switch (message.GetOp()) { + case BufferHubRPC::ConsumerAcquire::Opcode: + OnConsumerAcquire(message); + break; case BufferHubRPC::ConsumerRelease::Opcode: OnConsumerRelease(message, {}); break; @@ -70,7 +74,7 @@ bool ConsumerChannel::HandleMessage(Message& message) { switch (message.GetOp()) { case BufferHubRPC::GetBuffer::Opcode: DispatchRemoteMethod<BufferHubRPC::GetBuffer>( - *producer, &ProducerChannel::OnGetBuffer, message); + *this, &ConsumerChannel::OnGetBuffer, message); return true; case BufferHubRPC::NewConsumer::Opcode: @@ -98,9 +102,18 @@ bool ConsumerChannel::HandleMessage(Message& message) { } } -Status<std::pair<BorrowedFence, ConsumerChannel::MetaData>> -ConsumerChannel::OnConsumerAcquire(Message& message, - std::size_t metadata_size) { +Status<BufferDescription<BorrowedHandle>> ConsumerChannel::OnGetBuffer( + Message& /*message*/) { + ATRACE_NAME("ConsumerChannel::OnGetBuffer"); + ALOGD_IF(TRACE, "ConsumerChannel::OnGetBuffer: buffer=%d", buffer_id()); + if (auto producer = GetProducer()) { + return {producer->GetBuffer(consumer_state_bit_)}; + } else { + return ErrorStatus(EPIPE); + } +} + +Status<LocalFence> ConsumerChannel::OnConsumerAcquire(Message& message) { ATRACE_NAME("ConsumerChannel::OnConsumerAcquire"); auto producer = GetProducer(); if (!producer) @@ -114,7 +127,7 @@ ConsumerChannel::OnConsumerAcquire(Message& message, producer->buffer_id()); return ErrorStatus(EBUSY); } else { - auto status = producer->OnConsumerAcquire(message, metadata_size); + auto status = producer->OnConsumerAcquire(message); if (status) { ClearAvailable(); acquired_ = true; diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/consumer_channel.h index 208a002272..55cf96920d 100644 --- a/services/vr/bufferhubd/consumer_channel.h +++ b/services/vr/bufferhubd/consumer_channel.h @@ -12,32 +12,35 @@ namespace dvr { // Consumer channels are attached to a Producer channel class ConsumerChannel : public BufferHubChannel { public: + using BorrowedHandle = pdx::BorrowedHandle; using Channel = pdx::Channel; using Message = pdx::Message; ConsumerChannel(BufferHubService* service, int buffer_id, int channel_id, + uint64_t consumer_state_bit, const std::shared_ptr<Channel> producer); ~ConsumerChannel() override; bool HandleMessage(Message& message) override; void HandleImpulse(Message& message) override; + uint64_t consumer_state_bit() const { return consumer_state_bit_; } BufferInfo GetBufferInfo() const override; bool OnProducerPosted(); void OnProducerClosed(); private: - using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>; - std::shared_ptr<ProducerChannel> GetProducer() const; - pdx::Status<std::pair<BorrowedFence, MetaData>> OnConsumerAcquire( - Message& message, std::size_t metadata_size); + pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message); + + pdx::Status<LocalFence> OnConsumerAcquire(Message& message); pdx::Status<void> OnConsumerRelease(Message& message, LocalFence release_fence); pdx::Status<void> OnConsumerSetIgnore(Message& message, bool ignore); + uint64_t consumer_state_bit_{0}; bool acquired_{false}; bool released_{true}; bool ignored_{false}; // True if we are ignoring events. diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp index f447e00c31..4d430012f1 100644 --- a/services/vr/bufferhubd/consumer_queue_channel.cpp +++ b/services/vr/bufferhubd/consumer_queue_channel.cpp @@ -15,10 +15,11 @@ namespace dvr { ConsumerQueueChannel::ConsumerQueueChannel( BufferHubService* service, int buffer_id, int channel_id, - const std::shared_ptr<Channel>& producer) + const std::shared_ptr<Channel>& producer, bool silent) : BufferHubChannel(service, buffer_id, channel_id, kConsumerQueueType), producer_(producer), - capacity_(0) { + capacity_(0), + silent_(silent) { GetProducer()->AddConsumer(this); } @@ -83,23 +84,30 @@ BufferHubChannel::BufferInfo ConsumerQueueChannel::GetBufferInfo() const { void ConsumerQueueChannel::RegisterNewBuffer( const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot) { ALOGD_IF(TRACE, - "ConsumerQueueChannel::RegisterNewBuffer: buffer_id=%d slot=%zu", - producer_channel->buffer_id(), slot); - pending_buffer_slots_.emplace(producer_channel, slot); - - // Signal the client that there is new buffer available throught POLLIN. - SignalAvailable(); + "ConsumerQueueChannel::RegisterNewBuffer: queue_id=%d buffer_id=%d " + "slot=%zu silent=%d", + buffer_id(), producer_channel->buffer_id(), slot, silent_); + // Only register buffers if the queue is not silent. + if (!silent_) { + pending_buffer_slots_.emplace(producer_channel, slot); + + // Signal the client that there is new buffer available. + SignalAvailable(); + } } Status<std::vector<std::pair<RemoteChannelHandle, size_t>>> ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) { std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles; ATRACE_NAME("ConsumerQueueChannel::OnConsumerQueueImportBuffers"); - ALOGD_IF( - TRACE, - "ConsumerQueueChannel::OnConsumerQueueImportBuffers number of buffers to " - "import: %zu", - pending_buffer_slots_.size()); + ALOGD_IF(TRACE, + "ConsumerQueueChannel::OnConsumerQueueImportBuffers: " + "pending_buffer_slots=%zu", + pending_buffer_slots_.size()); + + // Indicate this is a silent queue that will not import buffers. + if (silent_) + return ErrorStatus(EBADR); while (!pending_buffer_slots_.empty()) { auto producer_channel = pending_buffer_slots_.front().first.lock(); diff --git a/services/vr/bufferhubd/consumer_queue_channel.h b/services/vr/bufferhubd/consumer_queue_channel.h index aa3f531c7f..8437c4cd04 100644 --- a/services/vr/bufferhubd/consumer_queue_channel.h +++ b/services/vr/bufferhubd/consumer_queue_channel.h @@ -19,7 +19,7 @@ class ConsumerQueueChannel : public BufferHubChannel { using RemoteChannelHandle = pdx::RemoteChannelHandle; ConsumerQueueChannel(BufferHubService* service, int buffer_id, int channel_id, - const std::shared_ptr<Channel>& producer); + const std::shared_ptr<Channel>& producer, bool silent); ~ConsumerQueueChannel() override; bool HandleMessage(Message& message) override; @@ -54,6 +54,10 @@ class ConsumerQueueChannel : public BufferHubChannel { // Tracks how many buffers have this queue imported. size_t capacity_; + // A silent queue does not signal or export buffers. It is only used to spawn + // another consumer queue. + bool silent_; + ConsumerQueueChannel(const ConsumerQueueChannel&) = delete; void operator=(const ConsumerQueueChannel&) = delete; }; diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp index b2db795717..716db5eeac 100644 --- a/services/vr/bufferhubd/producer_channel.cpp +++ b/services/vr/bufferhubd/producer_channel.cpp @@ -2,6 +2,8 @@ #include <log/log.h> #include <sync/sync.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> #include <sys/poll.h> #include <utils/Trace.h> @@ -24,24 +26,88 @@ using android::pdx::rpc::WrapBuffer; namespace android { namespace dvr { +namespace { + +static inline uint64_t FindNextClearedBit(uint64_t bits) { + return ~bits - (~bits & (~bits - 1)); +} + +} // namespace + ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t meta_size_bytes, + uint64_t usage, size_t user_metadata_size, int* error) : BufferHubChannel(service, channel_id, channel_id, kProducerType), pending_consumers_(0), producer_owns_(true), - meta_size_bytes_(meta_size_bytes), - meta_(meta_size_bytes ? new uint8_t[meta_size_bytes] : nullptr) { - const int ret = buffer_.Alloc(width, height, layer_count, format, usage); - if (ret < 0) { + user_metadata_size_(user_metadata_size), + metadata_buf_size_(BufferHubDefs::kMetadataHeaderSize + + user_metadata_size) { + if (int ret = buffer_.Alloc(width, height, layer_count, format, usage)) { ALOGE("ProducerChannel::ProducerChannel: Failed to allocate buffer: %s", strerror(-ret)); *error = ret; return; } + if (int ret = metadata_buffer_.Alloc(metadata_buf_size_, /*height=*/1, + /*layer_count=*/1, + BufferHubDefs::kMetadataFormat, + BufferHubDefs::kMetadataUsage)) { + ALOGE("ProducerChannel::ProducerChannel: Failed to allocate metadata: %s", + strerror(-ret)); + *error = ret; + return; + } + + void* metadata_ptr = nullptr; + if (int ret = metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0, + /*y=*/0, metadata_buf_size_, + /*height=*/1, &metadata_ptr)) { + ALOGE("ProducerChannel::ProducerChannel: Failed to lock metadata."); + *error = -ret; + return; + } + metadata_header_ = + reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr); + + // Using placement new here to reuse shared memory instead of new allocation + // and also initialize the value to zero. + buffer_state_ = + new (&metadata_header_->buffer_state) std::atomic<uint64_t>(0); + fence_state_ = + new (&metadata_header_->fence_state) std::atomic<uint64_t>(0); + + acquire_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC)); + release_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC)); + if (!acquire_fence_fd_ || !release_fence_fd_) { + ALOGE("ProducerChannel::ProducerChannel: Failed to create shared fences."); + *error = -EIO; + return; + } + + dummy_fence_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (!dummy_fence_fd_) { + ALOGE("ProducerChannel::ProducerChannel: Failed to create dummy fences."); + *error = -EIO; + return; + } + + epoll_event event; + event.events = 0; + event.data.u64 = 0ULL; + if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_ADD, dummy_fence_fd_.Get(), + &event) < 0) { + ALOGE( + "ProducerChannel::ProducerChannel: Failed to modify the shared " + "release fence to include the dummy fence: %s", + strerror(errno)); + *error = -EIO; + return; + } + // Success. *error = 0; } @@ -49,11 +115,11 @@ ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id, Status<std::shared_ptr<ProducerChannel>> ProducerChannel::Create( BufferHubService* service, int channel_id, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, - size_t meta_size_bytes) { + size_t user_metadata_size) { int error; std::shared_ptr<ProducerChannel> producer( new ProducerChannel(service, channel_id, width, height, layer_count, - format, usage, meta_size_bytes, &error)); + format, usage, user_metadata_size, &error)); if (error < 0) return ErrorStatus(-error); else @@ -62,16 +128,24 @@ Status<std::shared_ptr<ProducerChannel>> ProducerChannel::Create( ProducerChannel::~ProducerChannel() { ALOGD_IF(TRACE, - "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d", - channel_id(), buffer_id()); + "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d " + "state=%" PRIx64 ".", + channel_id(), buffer_id(), buffer_state_->load()); for (auto consumer : consumer_channels_) consumer->OnProducerClosed(); } BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const { + // Derive the mask of signaled buffers in this producer / consumer set. + uint64_t signaled_mask = signaled() ? BufferHubDefs::kProducerStateBit : 0; + for (const ConsumerChannel* consumer : consumer_channels_) { + signaled_mask |= consumer->signaled() ? consumer->consumer_state_bit() : 0; + } + return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(), buffer_.height(), buffer_.layer_count(), buffer_.format(), - buffer_.usage(), name_); + buffer_.usage(), pending_consumers_, buffer_state_->load(), + signaled_mask, metadata_header_->queue_index, name_); } void ProducerChannel::HandleImpulse(Message& message) { @@ -80,6 +154,9 @@ void ProducerChannel::HandleImpulse(Message& message) { case BufferHubRPC::ProducerGain::Opcode: OnProducerGain(message); break; + case BufferHubRPC::ProducerPost::Opcode: + OnProducerPost(message, {}); + break; } } @@ -121,16 +198,26 @@ bool ProducerChannel::HandleMessage(Message& message) { } } -Status<NativeBufferHandle<BorrowedHandle>> ProducerChannel::OnGetBuffer( +BufferDescription<BorrowedHandle> ProducerChannel::GetBuffer( + uint64_t buffer_state_bit) { + return { + buffer_, metadata_buffer_, buffer_id(), + buffer_state_bit, acquire_fence_fd_.Borrow(), release_fence_fd_.Borrow()}; +} + +Status<BufferDescription<BorrowedHandle>> ProducerChannel::OnGetBuffer( Message& /*message*/) { ATRACE_NAME("ProducerChannel::OnGetBuffer"); - ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d", buffer_id()); - return {NativeBufferHandle<BorrowedHandle>(buffer_, buffer_id())}; + ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d, state=%" PRIx64 ".", + buffer_id(), buffer_state_->load()); + return {GetBuffer(BufferHubDefs::kProducerStateBit)}; } Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) { ATRACE_NAME("ProducerChannel::CreateConsumer"); - ALOGD_IF(TRACE, "ProducerChannel::CreateConsumer: buffer_id=%d", buffer_id()); + ALOGD_IF(TRACE, + "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d", + buffer_id(), producer_owns_); int channel_id; auto status = message.PushChannel(0, nullptr, &channel_id); @@ -141,8 +228,21 @@ Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) { return ErrorStatus(ENOMEM); } - auto consumer = std::make_shared<ConsumerChannel>( - service(), buffer_id(), channel_id, shared_from_this()); + // Try find the next consumer state bit which has not been claimed by any + // consumer yet. + uint64_t consumer_state_bit = FindNextClearedBit( + active_consumer_bit_mask_ | orphaned_consumer_bit_mask_ | + BufferHubDefs::kProducerStateBit); + if (consumer_state_bit == 0ULL) { + ALOGE( + "ProducerChannel::CreateConsumer: reached the maximum mumber of " + "consumers per producer: 63."); + return ErrorStatus(E2BIG); + } + + auto consumer = + std::make_shared<ConsumerChannel>(service(), buffer_id(), channel_id, + consumer_state_bit, shared_from_this()); const auto channel_status = service()->SetChannel(channel_id, consumer); if (!channel_status) { ALOGE( @@ -152,12 +252,14 @@ Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) { return ErrorStatus(ENOMEM); } - if (!producer_owns_) { + if (!producer_owns_ && + !BufferHubDefs::IsBufferReleased(buffer_state_->load())) { // Signal the new consumer when adding it to a posted producer. if (consumer->OnProducerPosted()) pending_consumers_++; } + active_consumer_bit_mask_ |= consumer_state_bit; return {status.take()}; } @@ -168,8 +270,7 @@ Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) { } Status<void> ProducerChannel::OnProducerPost( - Message&, LocalFence acquire_fence, - BufferWrapper<std::vector<std::uint8_t>> metadata) { + Message&, LocalFence acquire_fence) { ATRACE_NAME("ProducerChannel::OnProducerPost"); ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id()); if (!producer_owns_) { @@ -177,27 +278,45 @@ Status<void> ProducerChannel::OnProducerPost( return ErrorStatus(EBUSY); } - if (meta_size_bytes_ != metadata.size()) { - ALOGD_IF(TRACE, - "ProducerChannel::OnProducerPost: Expected meta_size_bytes=%zu " - "got size=%zu", - meta_size_bytes_, metadata.size()); - return ErrorStatus(EINVAL); + epoll_event event; + event.events = 0; + event.data.u64 = 0ULL; + int ret = epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD, + dummy_fence_fd_.Get(), &event); + ALOGE_IF(ret < 0, + "ProducerChannel::OnProducerPost: Failed to modify the shared " + "release fence to include the dummy fence: %s", + strerror(errno)); + + eventfd_t dummy_fence_count = 0ULL; + if (eventfd_read(dummy_fence_fd_.Get(), &dummy_fence_count) < 0) { + const int error = errno; + if (error != EAGAIN) { + ALOGE( + "ProducerChannel::ProducerChannel: Failed to read dummy fence, " + "error: %s", + strerror(error)); + return ErrorStatus(error); + } } - std::copy(metadata.begin(), metadata.end(), meta_.get()); + ALOGW_IF(dummy_fence_count > 0, + "ProducerChannel::ProducerChannel: %" PRIu64 + " dummy fence(s) was signaled during last release/gain cycle " + "buffer_id=%d.", + dummy_fence_count, buffer_id()); + post_fence_ = std::move(acquire_fence); producer_owns_ = false; - // Signal any interested consumers. If there are none, automatically release - // the buffer. + // Signal any interested consumers. If there are none, the buffer will stay + // in posted state until a consumer comes online. This behavior guarantees + // that no frame is silently dropped. pending_consumers_ = 0; for (auto consumer : consumer_channels_) { if (consumer->OnProducerPosted()) pending_consumers_++; } - if (pending_consumers_ == 0) - SignalAvailable(); ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers", pending_consumers_); @@ -214,8 +333,13 @@ Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) { } // There are still pending consumers, return busy. - if (pending_consumers_ > 0) + if (pending_consumers_ > 0) { + ALOGE( + "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that " + "still has %d pending consumer(s).", + buffer_id(), pending_consumers_); return ErrorStatus(EBUSY); + } ClearAvailable(); producer_owns_ = true; @@ -223,9 +347,7 @@ Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) { return {std::move(returned_fence_)}; } -Status<std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>> -ProducerChannel::OnConsumerAcquire(Message& /*message*/, - std::size_t metadata_size) { +Status<LocalFence> ProducerChannel::OnConsumerAcquire(Message& /*message*/) { ATRACE_NAME("ProducerChannel::OnConsumerAcquire"); ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d", buffer_id()); @@ -236,12 +358,7 @@ ProducerChannel::OnConsumerAcquire(Message& /*message*/, // Return a borrowed fd to avoid unnecessary duplication of the underlying fd. // Serialization just needs to read the handle. - if (metadata_size == 0) - return {std::make_pair(post_fence_.borrow(), - WrapBuffer<std::uint8_t>(nullptr, 0))}; - else - return {std::make_pair(post_fence_.borrow(), - WrapBuffer(meta_.get(), meta_size_bytes_))}; + return {std::move(post_fence_)}; } Status<void> ProducerChannel::OnConsumerRelease(Message&, @@ -273,17 +390,75 @@ Status<void> ProducerChannel::OnConsumerRelease(Message&, } OnConsumerIgnored(); + if (pending_consumers_ == 0) { + // Clear the producer bit atomically to transit into released state. This + // has to done by BufferHub as it requries synchronization among all + // consumers. + BufferHubDefs::ModifyBufferState(buffer_state_, + BufferHubDefs::kProducerStateBit, 0ULL); + ALOGD_IF(TRACE, + "ProducerChannel::OnConsumerRelease: releasing last consumer: " + "buffer_id=%d state=%" PRIx64 ".", + buffer_id(), buffer_state_->load()); + + if (orphaned_consumer_bit_mask_) { + ALOGW( + "ProducerChannel::OnConsumerRelease: orphaned buffer detected " + "during the this acquire/release cycle: id=%d orphaned=0x%" PRIx64 + " queue_index=%" PRIu64 ".", + buffer_id(), orphaned_consumer_bit_mask_, + metadata_header_->queue_index); + orphaned_consumer_bit_mask_ = 0; + } + + SignalAvailable(); + } + + ALOGE_IF(pending_consumers_ && + BufferHubDefs::IsBufferReleased(buffer_state_->load()), + "ProducerChannel::OnConsumerRelease: buffer state inconsistent: " + "pending_consumers=%d, buffer buffer is in releaed state.", + pending_consumers_); return {}; } void ProducerChannel::OnConsumerIgnored() { - if (!--pending_consumers_) - SignalAvailable(); + if (pending_consumers_ == 0) { + ALOGE("ProducerChannel::OnConsumerIgnored: no pending consumer."); + return; + } + + --pending_consumers_; ALOGD_IF(TRACE, "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left", buffer_id(), pending_consumers_); } +void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) { + // Ignore the orphaned consumer. + OnConsumerIgnored(); + + const uint64_t consumer_state_bit = channel->consumer_state_bit(); + ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_bit, + "ProducerChannel::OnConsumerOrphaned: Consumer " + "(consumer_state_bit=%" PRIx64 ") is already orphaned.", + consumer_state_bit); + orphaned_consumer_bit_mask_ |= consumer_state_bit; + + // Atomically clear the fence state bit as an orphaned consumer will never + // signal a release fence. Also clear the buffer state as it won't be released + // as well. + fence_state_->fetch_and(~consumer_state_bit); + BufferHubDefs::ModifyBufferState(buffer_state_, consumer_state_bit, 0ULL); + + ALOGW( + "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer " + "buffer_id=%d consumer_state_bit=%" PRIx64 " queue_index=%" PRIu64 + " buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".", + buffer_id(), consumer_state_bit, metadata_header_->queue_index, + buffer_state_->load(), fence_state_->load()); +} + Status<void> ProducerChannel::OnProducerMakePersistent(Message& message, const std::string& name, int user_id, @@ -335,6 +510,40 @@ void ProducerChannel::AddConsumer(ConsumerChannel* channel) { void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) { consumer_channels_.erase( std::find(consumer_channels_.begin(), consumer_channels_.end(), channel)); + active_consumer_bit_mask_ &= ~channel->consumer_state_bit(); + + const uint64_t buffer_state = buffer_state_->load(); + if (BufferHubDefs::IsBufferPosted(buffer_state) || + BufferHubDefs::IsBufferAcquired(buffer_state)) { + // The consumer client is being destoryed without releasing. This could + // happen in corner cases when the consumer crashes. Here we mark it + // orphaned before remove it from producer. + OnConsumerOrphaned(channel); + } + + if (BufferHubDefs::IsBufferReleased(buffer_state) || + BufferHubDefs::IsBufferGained(buffer_state)) { + // The consumer is being close while it is suppose to signal a release + // fence. Signal the dummy fence here. + if (fence_state_->load() & channel->consumer_state_bit()) { + epoll_event event; + event.events = EPOLLIN; + event.data.u64 = channel->consumer_state_bit(); + if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD, + dummy_fence_fd_.Get(), &event) < 0) { + ALOGE( + "ProducerChannel::RemoveConsumer: Failed to modify the shared " + "release fence to include the dummy fence: %s", + strerror(errno)); + return; + } + ALOGW( + "ProducerChannel::RemoveConsumer: signal dummy release fence " + "buffer_id=%d", + buffer_id()); + eventfd_write(dummy_fence_fd_.Get(), 1); + } + } } // Returns true if either the user or group ids match the owning ids or both @@ -350,10 +559,12 @@ bool ProducerChannel::CheckAccess(int euid, int egid) { // Returns true if the given parameters match the underlying buffer parameters. bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t meta_size_bytes) { - return meta_size_bytes == meta_size_bytes_ && buffer_.width() == width && - buffer_.height() == height && buffer_.layer_count() == layer_count && - buffer_.format() == format && buffer_.usage() == usage; + uint64_t usage, + size_t user_metadata_size) { + return user_metadata_size == user_metadata_size_ && + buffer_.width() == width && buffer_.height() == height && + buffer_.layer_count() == layer_count && buffer_.format() == format && + buffer_.usage() == usage; } } // namespace dvr diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h index 5ada47880e..e280f4de8b 100644 --- a/services/vr/bufferhubd/producer_channel.h +++ b/services/vr/bufferhubd/producer_channel.h @@ -33,7 +33,7 @@ class ProducerChannel : public BufferHubChannel { static pdx::Status<std::shared_ptr<ProducerChannel>> Create( BufferHubService* service, int channel_id, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, - size_t meta_size_bytes); + size_t user_metadata_size); ~ProducerChannel() override; @@ -42,24 +42,25 @@ class ProducerChannel : public BufferHubChannel { BufferInfo GetBufferInfo() const override; - pdx::Status<NativeBufferHandle<BorrowedHandle>> OnGetBuffer(Message& message); + BufferDescription<BorrowedHandle> GetBuffer(uint64_t buffer_state_bit); pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message); pdx::Status<RemoteChannelHandle> OnNewConsumer(Message& message); - pdx::Status<std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>> - OnConsumerAcquire(Message& message, std::size_t metadata_size); + pdx::Status<LocalFence> OnConsumerAcquire(Message& message); pdx::Status<void> OnConsumerRelease(Message& message, LocalFence release_fence); void OnConsumerIgnored(); + void OnConsumerOrphaned(ConsumerChannel* channel); void AddConsumer(ConsumerChannel* channel); void RemoveConsumer(ConsumerChannel* channel); bool CheckAccess(int euid, int egid); bool CheckParameters(uint32_t width, uint32_t height, uint32_t layer_count, - uint32_t format, uint64_t usage, size_t meta_size_bytes); + uint32_t format, uint64_t usage, + size_t user_metadata_size); pdx::Status<void> OnProducerMakePersistent(Message& message, const std::string& name, @@ -74,11 +75,28 @@ class ProducerChannel : public BufferHubChannel { IonBuffer buffer_; + // IonBuffer that is shared between bufferhubd, producer, and consumers. + IonBuffer metadata_buffer_; + BufferHubDefs::MetadataHeader* metadata_header_ = nullptr; + std::atomic<uint64_t>* buffer_state_ = nullptr; + std::atomic<uint64_t>* fence_state_ = nullptr; + + // All active consumer bits. Valid bits are the lower 63 bits, while the + // highest bit is reserved for the producer and should not be set. + uint64_t active_consumer_bit_mask_{0ULL}; + // All orphaned consumer bits. Valid bits are the lower 63 bits, while the + // highest bit is reserved for the producer and should not be set. + uint64_t orphaned_consumer_bit_mask_{0ULL}; + bool producer_owns_; LocalFence post_fence_; LocalFence returned_fence_; - size_t meta_size_bytes_; - std::unique_ptr<uint8_t[]> meta_; + size_t user_metadata_size_; // size of user requested buffer buffer size. + size_t metadata_buf_size_; // size of the ion buffer that holds metadata. + + pdx::LocalHandle acquire_fence_fd_; + pdx::LocalHandle release_fence_fd_; + pdx::LocalHandle dummy_fence_fd_; static constexpr int kNoCheckId = -1; static constexpr int kUseCallerId = 0; @@ -92,11 +110,10 @@ class ProducerChannel : public BufferHubChannel { ProducerChannel(BufferHubService* service, int channel, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t meta_size_bytes, int* error); + uint64_t usage, size_t user_metadata_size, int* error); - pdx::Status<void> OnProducerPost( - Message& message, LocalFence acquire_fence, - BufferWrapper<std::vector<std::uint8_t>> metadata); + pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message); + pdx::Status<void> OnProducerPost(Message& message, LocalFence acquire_fence); pdx::Status<LocalFence> OnProducerGain(Message& message); ProducerChannel(const ProducerChannel&) = delete; diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp index b8bb728b70..c0c48c2dc1 100644 --- a/services/vr/bufferhubd/producer_queue_channel.cpp +++ b/services/vr/bufferhubd/producer_queue_channel.cpp @@ -7,8 +7,8 @@ using android::pdx::ErrorStatus; using android::pdx::Message; -using android::pdx::Status; using android::pdx::RemoteChannelHandle; +using android::pdx::Status; using android::pdx::rpc::DispatchRemoteMethod; namespace android { @@ -96,10 +96,12 @@ BufferHubChannel::BufferInfo ProducerQueueChannel::GetBufferInfo() const { } Status<RemoteChannelHandle> ProducerQueueChannel::OnCreateConsumerQueue( - Message& message) { + Message& message, bool silent) { ATRACE_NAME("ProducerQueueChannel::OnCreateConsumerQueue"); - ALOGD_IF(TRACE, "ProducerQueueChannel::OnCreateConsumerQueue: channel_id=%d", - channel_id()); + ALOGD_IF( + TRACE, + "ProducerQueueChannel::OnCreateConsumerQueue: channel_id=%d slient=%d", + channel_id(), silent); int channel_id; auto status = message.PushChannel(0, nullptr, &channel_id); @@ -112,7 +114,7 @@ Status<RemoteChannelHandle> ProducerQueueChannel::OnCreateConsumerQueue( } auto consumer_queue_channel = std::make_shared<ConsumerQueueChannel>( - service(), buffer_id(), channel_id, shared_from_this()); + service(), buffer_id(), channel_id, shared_from_this(), silent); // Register the existing buffers with the new consumer queue. for (size_t slot = 0; slot < BufferHubRPC::kMaxQueueCapacity; slot++) { @@ -222,7 +224,7 @@ ProducerQueueChannel::AllocateBuffer(Message& message, uint32_t width, auto producer_channel_status = ProducerChannel::Create(service(), buffer_id, width, height, layer_count, - format, usage, config_.meta_size_bytes); + format, usage, config_.user_metadata_size); if (!producer_channel_status) { ALOGE( "ProducerQueueChannel::AllocateBuffer: Failed to create producer " diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/producer_queue_channel.h index fd519c55e0..e825f47774 100644 --- a/services/vr/bufferhubd/producer_queue_channel.h +++ b/services/vr/bufferhubd/producer_queue_channel.h @@ -26,7 +26,7 @@ class ProducerQueueChannel : public BufferHubChannel { // Returns a handle for the service channel, as well as the size of the // metadata associated with the queue. pdx::Status<pdx::RemoteChannelHandle> OnCreateConsumerQueue( - pdx::Message& message); + pdx::Message& message, bool silent); pdx::Status<QueueInfo> OnGetQueueInfo(pdx::Message& message); diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp index c31417bcc7..abe571ae9e 100644 --- a/services/vr/hardware_composer/impl/vr_composer_client.cpp +++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp @@ -161,6 +161,10 @@ void VrComposerClient::onHotplug(Display display, client_->onHotplug(display, connected); } +void VrComposerClient::onRefresh(Display display) { + client_->onRefresh(display); +} + Return<void> VrComposerClient::registerCallback( const sp<IComposerCallback>& callback) { return client_->registerCallback(callback); diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h index f492230215..dfc656a0f1 100644 --- a/services/vr/hardware_composer/impl/vr_composer_client.h +++ b/services/vr/hardware_composer/impl/vr_composer_client.h @@ -35,6 +35,7 @@ class VrComposerClient : public IVrComposerClient { virtual ~VrComposerClient(); void onHotplug(Display display, IComposerCallback::Connection connected); + void onRefresh(Display display); // IComposerClient Return<void> registerCallback(const sp<IComposerCallback>& callback) override; diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp index 861114dbca..fd271d0fe2 100644 --- a/services/vr/hardware_composer/impl/vr_hwc.cpp +++ b/services/vr/hardware_composer/impl/vr_hwc.cpp @@ -133,13 +133,14 @@ void HwcDisplay::GetChangedCompositionTypes( return lhs.info.z_order < rhs.info.z_order; }); - int first_client_layer = -1, last_client_layer = -1; + const size_t no_layer = std::numeric_limits<size_t>::max(); + size_t first_client_layer = no_layer, last_client_layer = no_layer; for (size_t i = 0; i < layers_.size(); ++i) { switch (layers_[i].composition_type) { case IComposerClient::Composition::SOLID_COLOR: case IComposerClient::Composition::CURSOR: case IComposerClient::Composition::SIDEBAND: - if (first_client_layer < 0) + if (first_client_layer == no_layer) first_client_layer = i; last_client_layer = i; @@ -231,7 +232,7 @@ VrHwc::VrHwc() {} VrHwc::~VrHwc() {} -bool VrHwc::hasCapability(Capability capability) const { return false; } +bool VrHwc::hasCapability(Capability /* capability */) const { return false; } void VrHwc::removeClient() { std::lock_guard<std::mutex> guard(mutex_); @@ -305,13 +306,15 @@ Error VrHwc::getActiveConfig(Display display, Config* outConfig) { return Error::NONE; } -Error VrHwc::getClientTargetSupport(Display display, uint32_t width, - uint32_t height, PixelFormat format, - Dataspace dataspace) { +Error VrHwc::getClientTargetSupport(Display /* display */, uint32_t /* width */, + uint32_t /* height */, + PixelFormat /* format */, + Dataspace /* dataspace */) { return Error::NONE; } -Error VrHwc::getColorModes(Display display, hidl_vec<ColorMode>* outModes) { +Error VrHwc::getColorModes(Display /* display */, + hidl_vec<ColorMode>* outModes) { std::vector<ColorMode> color_modes(1, ColorMode::NATIVE); *outModes = hidl_vec<ColorMode>(color_modes); return Error::NONE; @@ -378,7 +381,7 @@ Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) { return Error::NONE; } -Error VrHwc::getDisplayName(Display display, hidl_string* outName) { +Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) { *outName = hidl_string(); return Error::NONE; } @@ -408,7 +411,8 @@ Error VrHwc::getDozeSupport(Display display, bool* outSupport) { return Error::NONE; } -Error VrHwc::getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, +Error VrHwc::getHdrCapabilities(Display /* display */, + hidl_vec<Hdr>* /* outTypes */, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) { @@ -472,8 +476,8 @@ Error VrHwc::setColorTransform(Display display, const float* matrix, } Error VrHwc::setClientTarget(Display display, buffer_handle_t target, - int32_t acquireFence, int32_t dataspace, - const std::vector<hwc_rect_t>& damage) { + int32_t acquireFence, int32_t /* dataspace */, + const std::vector<hwc_rect_t>& /* damage */) { base::unique_fd fence(acquireFence); std::lock_guard<std::mutex> guard(mutex_); auto display_ptr = FindDisplay(display); @@ -489,7 +493,7 @@ Error VrHwc::setClientTarget(Display display, buffer_handle_t target, return Error::NONE; } -Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer, +Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */, int32_t releaseFence) { base::unique_fd fence(releaseFence); std::lock_guard<std::mutex> guard(mutex_); @@ -504,8 +508,9 @@ Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer, Error VrHwc::validateDisplay( Display display, std::vector<Layer>* outChangedLayers, std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, - std::vector<uint32_t>* outRequestMasks) { + uint32_t* /* outDisplayRequestMask */, + std::vector<Layer>* /* outRequestedLayers */, + std::vector<uint32_t>* /* outRequestMasks */) { std::lock_guard<std::mutex> guard(mutex_); auto display_ptr = FindDisplay(display); if (!display_ptr) @@ -516,7 +521,7 @@ Error VrHwc::validateDisplay( return Error::NONE; } -Error VrHwc::acceptDisplayChanges(Display display) { return Error::NONE; } +Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; } Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers, @@ -708,8 +713,8 @@ Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) { return Error::NONE; } -Error VrHwc::setLayerSidebandStream(Display display, Layer layer, - buffer_handle_t stream) { +Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */, + buffer_handle_t /* stream */) { std::lock_guard<std::mutex> guard(mutex_); if (!FindDisplay(display)) return Error::BAD_DISPLAY; @@ -850,6 +855,14 @@ Return<void> VrHwc::createClient(createClient_cb hidl_cb) { return Void(); } +void VrHwc::ForceDisplaysRefresh() { + std::lock_guard<std::mutex> guard(mutex_); + if (client_ != nullptr) { + for (const auto& pair : displays_) + client_.promote()->onRefresh(pair.first); + } +} + void VrHwc::RegisterObserver(Observer* observer) { std::lock_guard<std::mutex> guard(mutex_); if (observer_) diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h index 523cda3f69..fce9a063e0 100644 --- a/services/vr/hardware_composer/impl/vr_hwc.h +++ b/services/vr/hardware_composer/impl/vr_hwc.h @@ -103,6 +103,7 @@ class ComposerView { virtual ~ComposerView() {} + virtual void ForceDisplaysRefresh() = 0; virtual void RegisterObserver(Observer* observer) = 0; virtual void UnregisterObserver(Observer* observer) = 0; }; @@ -288,6 +289,7 @@ class VrHwc : public IComposer, public ComposerBase, public ComposerView { Return<void> createClient(createClient_cb hidl_cb) override; // ComposerView: + void ForceDisplaysRefresh() override; void RegisterObserver(Observer* observer) override; void UnregisterObserver(Observer* observer) override; @@ -295,7 +297,6 @@ class VrHwc : public IComposer, public ComposerBase, public ComposerView { HwcDisplay* FindDisplay(Display display); wp<VrComposerClient> client_; - sp<IComposerCallback> callbacks_; // Guard access to internal state from binder threads. std::mutex mutex_; diff --git a/services/vr/hardware_composer/tests/vr_composer_test.cpp b/services/vr/hardware_composer/tests/vr_composer_test.cpp index d082f4bc92..2e70928662 100644 --- a/services/vr/hardware_composer/tests/vr_composer_test.cpp +++ b/services/vr/hardware_composer/tests/vr_composer_test.cpp @@ -10,6 +10,24 @@ namespace { const char kVrDisplayName[] = "VrDisplay_Test"; +class TestComposerView : public ComposerView { + public: + TestComposerView() {} + ~TestComposerView() override = default; + + size_t display_refresh_count() const { return display_refresh_count_; } + + void ForceDisplaysRefresh() override { display_refresh_count_++; } + void RegisterObserver(Observer* observer) override {} + void UnregisterObserver(Observer* observer) override {} + + TestComposerView(const TestComposerView&) = delete; + void operator=(const TestComposerView&) = delete; + + private: + size_t display_refresh_count_ = 0; +}; + class TestComposerCallback : public BnVrComposerCallback { public: TestComposerCallback() {} @@ -57,7 +75,7 @@ sp<GraphicBuffer> CreateBuffer() { class VrComposerTest : public testing::Test { public: - VrComposerTest() : composer_(new VrComposer()) {} + VrComposerTest() : composer_(new VrComposer(&composer_view_)) {} ~VrComposerTest() override = default; sp<IVrComposer> GetComposerProxy() const { @@ -72,6 +90,7 @@ class VrComposerTest : public testing::Test { } protected: + TestComposerView composer_view_; sp<VrComposer> composer_; VrComposerTest(const VrComposerTest&) = delete; @@ -89,7 +108,9 @@ TEST_F(VrComposerTest, TestWithoutObserver) { TEST_F(VrComposerTest, TestWithObserver) { sp<IVrComposer> composer = GetComposerProxy(); sp<TestComposerCallback> callback = new TestComposerCallback(); + ASSERT_EQ(0, composer_view_.display_refresh_count()); ASSERT_TRUE(composer->registerObserver(callback).isOk()); + ASSERT_EQ(1, composer_view_.display_refresh_count()); ComposerView::Frame frame; base::unique_fd fence = composer_->OnNewFrame(frame); diff --git a/services/vr/hardware_composer/vr_composer.cpp b/services/vr/hardware_composer/vr_composer.cpp index 36a313ae24..d93f370945 100644 --- a/services/vr/hardware_composer/vr_composer.cpp +++ b/services/vr/hardware_composer/vr_composer.cpp @@ -21,24 +21,36 @@ bool CheckPermission() { } // namespace -VrComposer::VrComposer() {} +VrComposer::VrComposer(ComposerView* composer_view) + : composer_view_(composer_view) { + composer_view_->RegisterObserver(this); +} -VrComposer::~VrComposer() {} +VrComposer::~VrComposer() { + composer_view_->UnregisterObserver(this); +} binder::Status VrComposer::registerObserver( const sp<IVrComposerCallback>& callback) { - std::lock_guard<std::mutex> guard(mutex_); + { + std::lock_guard<std::mutex> guard(mutex_); + + if (!CheckPermission()) + return binder::Status::fromStatusT(PERMISSION_DENIED); - if (!CheckPermission()) - return binder::Status::fromStatusT(PERMISSION_DENIED); + if (callback_.get()) { + ALOGE("Failed to register callback, already registered"); + return binder::Status::fromStatusT(ALREADY_EXISTS); + } - if (callback_.get()) { - ALOGE("Failed to register callback, already registered"); - return binder::Status::fromStatusT(ALREADY_EXISTS); + callback_ = callback; + IInterface::asBinder(callback_)->linkToDeath(this); } - callback_ = callback; - IInterface::asBinder(callback_)->linkToDeath(this); + // Don't take the lock to force display refresh otherwise it could end in a + // deadlock since HWC calls this with new frames and it has a lock of its own + // to serialize access to the display information. + composer_view_->ForceDisplaysRefresh(); return binder::Status::ok(); } diff --git a/services/vr/hardware_composer/vr_composer.h b/services/vr/hardware_composer/vr_composer.h index 7b580c6cba..1273352ad0 100644 --- a/services/vr/hardware_composer/vr_composer.h +++ b/services/vr/hardware_composer/vr_composer.h @@ -20,7 +20,7 @@ class VrComposer public ComposerView::Observer, public IBinder::DeathRecipient { public: - VrComposer(); + explicit VrComposer(ComposerView* composer_view); ~VrComposer() override; // BnVrComposer: @@ -40,6 +40,8 @@ class VrComposer sp<IVrComposerCallback> callback_; + ComposerView* composer_view_; // Not owned. + VrComposer(const VrComposer&) = delete; void operator=(const VrComposer&) = delete; }; diff --git a/services/vr/hardware_composer/vr_hardware_composer_service.cpp b/services/vr/hardware_composer/vr_hardware_composer_service.cpp index e36b0ae3f1..7701847120 100644 --- a/services/vr/hardware_composer/vr_hardware_composer_service.cpp +++ b/services/vr/hardware_composer/vr_hardware_composer_service.cpp @@ -36,8 +36,7 @@ int main() { "Failed to register service"); android::sp<android::dvr::VrComposer> composer = - new android::dvr::VrComposer(); - service->RegisterObserver(composer.get()); + new android::dvr::VrComposer(service.get()); android::sp<android::IServiceManager> sm(android::defaultServiceManager()); @@ -52,7 +51,5 @@ int main() { android::hardware::ProcessState::self()->startThreadPool(); android::hardware::IPCThreadState::self()->joinThreadPool(); - service->UnregisterObserver(composer.get()); - return 0; } diff --git a/services/vr/performanced/main.cpp b/services/vr/performanced/main.cpp index ca66c710d7..d7dc8f6afe 100644 --- a/services/vr/performanced/main.cpp +++ b/services/vr/performanced/main.cpp @@ -9,7 +9,7 @@ #include <sys/resource.h> #include <utils/threads.h> -#include <pdx/default_transport/service_dispatcher.h> +#include <pdx/service_dispatcher.h> #include <private/android_filesystem_config.h> #include "performance_service.h" @@ -58,7 +58,7 @@ int main(int /*argc*/, char** /*argv*/) { CHECK_ERROR(ret < 0, error, "Could not set capabilities: %s", strerror(errno)); - dispatcher = android::pdx::default_transport::ServiceDispatcher::Create(); + dispatcher = android::pdx::ServiceDispatcher::Create(); CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher."); service = android::dvr::PerformanceService::Create(); diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp index 4b9fbe047d..4c26671b11 100644 --- a/services/vr/performanced/performance_service.cpp +++ b/services/vr/performanced/performance_service.cpp @@ -22,13 +22,15 @@ using android::dvr::Task; using android::pdx::ErrorStatus; using android::pdx::Message; using android::pdx::Status; -using android::pdx::rpc::DispatchRemoteMethod; using android::pdx::default_transport::Endpoint; +using android::pdx::rpc::DispatchRemoteMethod; namespace { const char kCpuSetBasePath[] = "/dev/cpuset"; +const char kRootCpuSet[] = "/"; + constexpr unsigned long kTimerSlackForegroundNs = 50000; constexpr unsigned long kTimerSlackBackgroundNs = 40000000; @@ -123,22 +125,22 @@ PerformanceService::PerformanceService() // hack for now to put some form of permission logic in place while a longer // term solution is developed. using AllowRootSystem = - CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM>, - GroupId<AID_SYSTEM>>>; + CheckAnd<SameProcess, + CheckOr<UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>>; using AllowRootSystemGraphics = CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>, GroupId<AID_SYSTEM, AID_GRAPHICS>>>; using AllowRootSystemAudio = CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_AUDIO>, GroupId<AID_SYSTEM, AID_AUDIO>>>; - using AllowRootSystemTrusted = CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, - GroupId<AID_SYSTEM>>; + using AllowRootSystemTrusted = + CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>; partition_permission_check_ = AllowRootSystemTrusted::Check; // Setup the scheduler classes. // TODO(eieio): Replace this with a device-specific config file. - scheduler_classes_ = { + scheduler_policies_ = { {"audio:low", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, @@ -183,12 +185,14 @@ PerformanceService::PerformanceService() {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium + 2, - .permission_check = AllowRootSystemTrusted::Check}}, + .permission_check = AllowRootSystemTrusted::Check, + "/system/performance"}}, {"vr:app:render", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium + 1, - .permission_check = AllowRootSystemTrusted::Check}}, + .permission_check = AllowRootSystemTrusted::Check, + "/application/performance"}}, {"normal", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_NORMAL, @@ -219,14 +223,80 @@ std::string PerformanceService::DumpState(size_t /*max_length*/) { Status<void> PerformanceService::OnSetSchedulerPolicy( Message& message, pid_t task_id, const std::string& scheduler_policy) { - // Forward to scheduler class handler for now. In the future this method will - // subsume the others by unifying both scheduler class and cpu partiton into a - // single policy concept. ALOGI( "PerformanceService::OnSetSchedulerPolicy: task_id=%d " "scheduler_policy=%s", task_id, scheduler_policy.c_str()); - return OnSetSchedulerClass(message, task_id, scheduler_policy); + + Task task(task_id); + if (!task) { + ALOGE( + "PerformanceService::OnSetSchedulerPolicy: Unable to access /proc/%d " + "to gather task information.", + task_id); + return ErrorStatus(EINVAL); + } + + auto search = scheduler_policies_.find(scheduler_policy); + if (search != scheduler_policies_.end()) { + auto config = search->second; + + // Make sure the sending process is allowed to make the requested change to + // this task. + if (!config.IsAllowed(message, task)) + return ErrorStatus(EINVAL); + + // Get the thread group's cpu set. Policies that do not specify a cpuset + // should default to this cpuset. + std::string thread_group_cpuset; + Task thread_group{task.thread_group_id()}; + if (thread_group) { + thread_group_cpuset = thread_group.GetCpuSetPath(); + } else { + ALOGE( + "PerformanceService::OnSetSchedulerPolicy: Failed to get thread " + "group tgid=%d for task_id=%d", + task.thread_group_id(), task_id); + thread_group_cpuset = kRootCpuSet; + } + + std::string target_cpuset; + if (config.cpuset.empty()) { + target_cpuset = thread_group_cpuset; + } else { + target_cpuset = config.cpuset; + } + ALOGI("PerformanceService::OnSetSchedulerPolicy: Using cpuset=%s", + target_cpuset.c_str()); + + auto target_set = cpuset_.Lookup(target_cpuset); + if (target_set) { + auto attach_status = target_set->AttachTask(task_id); + ALOGW_IF(!attach_status, + "PerformanceService::OnSetSchedulerPolicy: Failed to attach " + "task=%d to cpuset=%s: %s", + task_id, target_cpuset.c_str(), + attach_status.GetErrorMessage().c_str()); + } else { + ALOGW( + "PerformanceService::OnSetSchedulerPolicy: Failed to lookup " + "cpuset=%s", + target_cpuset.c_str()); + } + + struct sched_param param; + param.sched_priority = config.priority; + + sched_setscheduler(task_id, config.scheduler_policy, ¶m); + prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id); + return {}; + } else { + ALOGE( + "PerformanceService::OnSetSchedulerPolicy: Invalid scheduler_policy=%s " + "requested by task=%d.", + scheduler_policy.c_str(), task_id); + return ErrorStatus(EINVAL); + } } Status<void> PerformanceService::OnSetCpuPartition( @@ -259,8 +329,8 @@ Status<void> PerformanceService::OnSetSchedulerClass( if (!task) return ErrorStatus(EINVAL); - auto search = scheduler_classes_.find(scheduler_class); - if (search != scheduler_classes_.end()) { + auto search = scheduler_policies_.find(scheduler_class); + if (search != scheduler_policies_.end()) { auto config = search->second; // Make sure the sending process is allowed to make the requested change to diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h index b28d94addb..6b519abac3 100644 --- a/services/vr/performanced/performance_service.h +++ b/services/vr/performanced/performance_service.h @@ -44,13 +44,13 @@ class PerformanceService : public pdx::ServiceBase<PerformanceService> { int sched_fifo_min_priority_; int sched_fifo_max_priority_; - // Scheduler class config type. - struct SchedulerClassConfig { + struct SchedulerPolicyConfig { unsigned long timer_slack; int scheduler_policy; int priority; std::function<bool(const pdx::Message& message, const Task& task)> permission_check; + std::string cpuset; // Check the permisison of the given task to use this scheduler class. If a // permission check function is not set then operations are only allowed on @@ -65,7 +65,7 @@ class PerformanceService : public pdx::ServiceBase<PerformanceService> { } }; - std::unordered_map<std::string, SchedulerClassConfig> scheduler_classes_; + std::unordered_map<std::string, SchedulerPolicyConfig> scheduler_policies_; std::function<bool(const pdx::Message& message, const Task& task)> partition_permission_check_; diff --git a/services/vr/performanced/performance_service_tests.cpp b/services/vr/performanced/performance_service_tests.cpp index 274a1b36d4..4065785426 100644 --- a/services/vr/performanced/performance_service_tests.cpp +++ b/services/vr/performanced/performance_service_tests.cpp @@ -1,24 +1,65 @@ #include <errno.h> #include <sched.h> +#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <condition_variable> #include <cstdlib> +#include <iostream> #include <mutex> +#include <sstream> #include <thread> +#include <utility> +#include <android-base/unique_fd.h> #include <dvr/performance_client_api.h> #include <gtest/gtest.h> #include <private/android_filesystem_config.h> +#include "stdio_filebuf.h" +#include "string_trim.h" +#include "unique_file.h" + +using android::dvr::Trim; +using android::dvr::UniqueFile; +using android::dvr::stdio_filebuf; + namespace { const char kTrustedUidEnvironmentVariable[] = "GTEST_TRUSTED_UID"; +const char kProcBase[] = "/proc"; + +std::pair<UniqueFile, int> OpenTaskFile(pid_t task_id, + const std::string& name) { + std::ostringstream stream; + stream << kProcBase << "/" << task_id << "/" << name; + + UniqueFile file{fopen(stream.str().c_str(), "r")}; + const int error = file ? 0 : errno; + return {std::move(file), error}; +} + +std::string GetTaskCpuSet(pid_t task_id) { + int error; + UniqueFile file; + + std::tie(file, error) = OpenTaskFile(task_id, "cpuset"); + if (!file) + return std::string("errno:") + strerror(error); + + stdio_filebuf<char> filebuf(file.get()); + std::istream file_stream(&filebuf); + + std::string line; + std::getline(file_stream, line); + return Trim(line); +} + } // anonymous namespace -TEST(DISABLED_PerformanceTest, SetCpuPartition) { +TEST(PerformanceTest, SetCpuPartition) { int error; // Test setting the the partition for the current task. @@ -59,13 +100,6 @@ TEST(DISABLED_PerformanceTest, SetCpuPartition) { } thread.join(); - // Test setting the partition for a task that isn't valid using - // the task id of the thread that we just joined. Technically the - // id could wrap around by the time we get here, but this is - // extremely unlikely. - error = dvrSetCpuPartition(task_id, "/application"); - EXPECT_EQ(-EINVAL, error); - // Test setting the partition for a task that doesn't belong to us. error = dvrSetCpuPartition(1, "/application"); EXPECT_EQ(-EINVAL, error); @@ -73,6 +107,10 @@ TEST(DISABLED_PerformanceTest, SetCpuPartition) { // Test setting the partition to one that doesn't exist. error = dvrSetCpuPartition(0, "/foobar"); EXPECT_EQ(-ENOENT, error); + + // Set the test back to the root partition. + error = dvrSetCpuPartition(0, "/"); + EXPECT_EQ(0, error); } TEST(PerformanceTest, SetSchedulerClass) { @@ -96,8 +134,6 @@ TEST(PerformanceTest, SetSchedulerClass) { EXPECT_EQ(-EINVAL, error); } -// This API mirrors SetSchedulerClass for now. Replace with with a more specific -// test once the policy API is fully implemented. TEST(PerformanceTest, SetSchedulerPolicy) { int error; @@ -115,6 +151,50 @@ TEST(PerformanceTest, SetSchedulerPolicy) { error = dvrSetSchedulerPolicy(0, "foobar"); EXPECT_EQ(-EINVAL, error); + + // Set the test back to the root partition. + error = dvrSetCpuPartition(0, "/"); + EXPECT_EQ(0, error); + + const std::string original_cpuset = GetTaskCpuSet(gettid()); + EXPECT_EQ("/", original_cpuset); + + error = dvrSetSchedulerPolicy(0, "vr:system:arp"); + EXPECT_EQ(0, error); + EXPECT_EQ(SCHED_FIFO | SCHED_RESET_ON_FORK, sched_getscheduler(0)); + + const std::string new_cpuset = GetTaskCpuSet(gettid()); + EXPECT_NE(original_cpuset, new_cpuset); + + // The cpuset for the thread group is now new_cpuset. Scheduler profiles that + // do not specify a cpuset should not change the cpuset of a thread, except to + // restore it to the thread group cpuset. + std::string thread_original_cpuset; + std::string thread_new_cpuset; + std::string thread_final_cpuset; + + std::thread thread{ + [&thread_original_cpuset, &thread_new_cpuset, &thread_final_cpuset]() { + thread_original_cpuset = GetTaskCpuSet(gettid()); + + int error = dvrSetSchedulerPolicy(0, "vr:app:render"); + EXPECT_EQ(0, error); + + thread_new_cpuset = GetTaskCpuSet(gettid()); + + error = dvrSetSchedulerPolicy(0, "normal"); + EXPECT_EQ(0, error); + + thread_final_cpuset = GetTaskCpuSet(gettid()); + }}; + thread.join(); + + EXPECT_EQ(new_cpuset, thread_original_cpuset); + EXPECT_NE(new_cpuset, thread_new_cpuset); + EXPECT_EQ(new_cpuset, thread_final_cpuset); + + error = dvrSetCpuPartition(0, original_cpuset.c_str()); + EXPECT_EQ(0, error); } TEST(PerformanceTest, SchedulerClassResetOnFork) { @@ -424,11 +504,11 @@ TEST(PerformanceTest, Permissions) { error = dvrSetSchedulerPolicy(0, "audio:high"); EXPECT_EQ(-EINVAL, error); error = dvrSetSchedulerPolicy(0, "graphics"); - EXPECT_EQ(0, error); + EXPECT_EQ(-EINVAL, error); error = dvrSetSchedulerPolicy(0, "graphics:low"); - EXPECT_EQ(0, error); + EXPECT_EQ(-EINVAL, error); error = dvrSetSchedulerPolicy(0, "graphics:high"); - EXPECT_EQ(0, error); + EXPECT_EQ(-EINVAL, error); error = dvrSetSchedulerPolicy(0, "sensors"); EXPECT_EQ(-EINVAL, error); error = dvrSetSchedulerPolicy(0, "sensors:low"); diff --git a/services/vr/performanced/task.cpp b/services/vr/performanced/task.cpp index 1175a7b12b..c2f078efb4 100644 --- a/services/vr/performanced/task.cpp +++ b/services/vr/performanced/task.cpp @@ -48,15 +48,18 @@ Task::Task(pid_t task_id) thread_count_(0), cpus_allowed_mask_(0) { task_fd_ = OpenTaskDirectory(task_id_); - ALOGE_IF(task_fd_.get() < 0, + const int error = errno; + ALOGE_IF(task_fd_.get() < 0 && error != EACCES, "Task::Task: Failed to open task directory for task_id=%d: %s", - task_id, strerror(errno)); - - ReadStatusFields(); - - ALOGD_IF(TRACE, "Task::Task: task_id=%d name=%s tgid=%d ppid=%d cpu_mask=%x", - task_id_, name_.c_str(), thread_group_id_, parent_process_id_, - cpus_allowed_mask_); + task_id, strerror(error)); + + if (IsValid()) { + ReadStatusFields(); + ALOGD_IF(TRACE, + "Task::Task: task_id=%d name=%s tgid=%d ppid=%d cpu_mask=%x", + task_id_, name_.c_str(), thread_group_id_, parent_process_id_, + cpus_allowed_mask_); + } } base::unique_fd Task::OpenTaskFile(const std::string& name) const { diff --git a/vulkan/Android.bp b/vulkan/Android.bp index 91c270e02b..6107088557 100644 --- a/vulkan/Android.bp +++ b/vulkan/Android.bp @@ -25,8 +25,22 @@ ndk_headers { cc_library_headers { name: "vulkan_headers", - export_include_dirs: ["include"], vendor_available: true, + header_libs: [ + "libcutils_headers", + "libhardware_headers", + ], + export_header_lib_headers: [ + "libcutils_headers", + "libhardware_headers", + ], + export_include_dirs: ["include"], +} + +cc_library_headers { + name: "vulkan_headers_ndk", + export_include_dirs: ["include"], + sdk_version: "24", } subdirs = [ diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index a346c0ac76..665a32b90c 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -640,11 +640,9 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); const VkSurfaceFormatKHR kWideColorFormats[] = { - {VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}, - {VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT}, - {VK_FORMAT_A2R10G10B10_UNORM_PACK32, + {VK_FORMAT_R8G8B8A8_UNORM, + VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}, + {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}, }; const uint32_t kNumWideColorFormats = diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index 6814ae611c..b4ca42c009 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -19,12 +19,12 @@ #include <inttypes.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <algorithm> #include <array> #include <log/log.h> -#include <utils/Errors.h> #include "null_driver_gen.h" |