summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/battery/Android.bp37
-rw-r--r--libs/battery/LongArrayMultiStateCounter.cpp79
-rw-r--r--libs/battery/LongArrayMultiStateCounter.h29
-rw-r--r--libs/battery/LongArrayMultiStateCounterTest.cpp69
-rw-r--r--libs/battery/MultiStateCounter.h297
-rw-r--r--libs/battery/MultiStateCounterTest.cpp268
-rw-r--r--libs/binder/include/binder/IInterface.h2
-rw-r--r--libs/binder/ndk/include_ndk/android/binder_ibinder.h2
-rw-r--r--libs/ftl/Android.bp5
-rw-r--r--libs/ftl/Flags_test.cpp20
-rw-r--r--libs/ftl/NamedEnum_test.cpp101
-rw-r--r--libs/ftl/cast_test.cpp200
-rw-r--r--libs/ftl/concat_test.cpp55
-rw-r--r--libs/ftl/enum_test.cpp164
-rw-r--r--libs/ftl/small_map_test.cpp280
-rw-r--r--libs/ftl/small_vector_test.cpp30
-rw-r--r--libs/ftl/static_vector_test.cpp15
-rw-r--r--libs/ftl/string_test.cpp187
-rw-r--r--libs/gralloc/types/Android.bp8
-rw-r--r--libs/gralloc/types/Gralloc4.cpp12
-rw-r--r--libs/gralloc/types/include/gralloctypes/Gralloc4.h11
-rw-r--r--libs/gralloc/types/tests/Gralloc4_test.cpp28
-rw-r--r--libs/gui/Android.bp9
-rw-r--r--libs/gui/BLASTBufferQueue.cpp349
-rw-r--r--libs/gui/DisplayEventDispatcher.cpp16
-rw-r--r--libs/gui/DisplayEventReceiver.cpp1
-rw-r--r--libs/gui/DisplayInfo.cpp70
-rw-r--r--libs/gui/FrameTimelineInfo.cpp5
-rw-r--r--libs/gui/IDisplayEventConnection.cpp80
-rw-r--r--libs/gui/IRegionSamplingListener.cpp64
-rw-r--r--libs/gui/ISurfaceComposer.cpp293
-rw-r--r--libs/gui/ITransactionCompletedListener.cpp3
-rw-r--r--libs/gui/LayerState.cpp179
-rw-r--r--libs/gui/Surface.cpp3
-rw-r--r--libs/gui/SurfaceComposerClient.cpp270
-rw-r--r--libs/gui/WindowInfo.cpp25
-rw-r--r--libs/gui/WindowInfosListenerReporter.cpp5
-rw-r--r--libs/gui/aidl/android/gui/BitTube.aidl (renamed from libs/ui/Size.cpp)11
-rw-r--r--libs/gui/aidl/android/gui/IDisplayEventConnection.aidl (renamed from libs/gui/include/gui/IDisplayEventConnection.h)42
-rw-r--r--libs/gui/aidl/android/gui/IRegionSamplingListener.aidl22
-rw-r--r--libs/gui/android/gui/DisplayInfo.aidl19
-rw-r--r--libs/gui/android/gui/IWindowInfosListener.aidl3
-rw-r--r--libs/gui/include/gui/BLASTBufferQueue.h47
-rw-r--r--libs/gui/include/gui/DisplayEventDispatcher.h24
-rw-r--r--libs/gui/include/gui/DisplayEventReceiver.h11
-rw-r--r--libs/gui/include/gui/DisplayInfo.h46
-rw-r--r--libs/gui/include/gui/FrameTimelineInfo.h3
-rw-r--r--libs/gui/include/gui/IRegionSamplingListener.h43
-rw-r--r--libs/gui/include/gui/ISurfaceComposer.h87
-rw-r--r--libs/gui/include/gui/ITransactionCompletedListener.h1
-rw-r--r--libs/gui/include/gui/LayerMetadata.h10
-rw-r--r--libs/gui/include/gui/LayerState.h105
-rw-r--r--libs/gui/include/gui/Surface.h4
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h87
-rw-r--r--libs/gui/include/gui/WindowInfo.h110
-rw-r--r--libs/gui/include/gui/WindowInfosListener.h4
-rw-r--r--libs/gui/include/gui/WindowInfosListenerReporter.h4
-rw-r--r--libs/gui/include/gui/test/CallbackUtils.h2
-rw-r--r--libs/gui/tests/Android.bp7
-rw-r--r--libs/gui/tests/BLASTBufferQueue_test.cpp273
-rw-r--r--libs/gui/tests/DisplayInfo_test.cpp49
-rw-r--r--libs/gui/tests/EndToEndNativeInputTest.cpp197
-rw-r--r--libs/gui/tests/RegionSampling_test.cpp7
-rw-r--r--libs/gui/tests/SamplingDemo.cpp7
-rw-r--r--libs/gui/tests/Surface_test.cpp36
-rw-r--r--libs/gui/tests/WindowInfo_test.cpp5
-rw-r--r--libs/input/Input.cpp334
-rw-r--r--libs/input/InputDevice.cpp10
-rw-r--r--libs/input/InputTransport.cpp111
-rw-r--r--libs/input/KeyLayoutMap.cpp77
-rw-r--r--libs/input/VelocityTracker.cpp140
-rw-r--r--libs/input/android/os/IInputConstants.aidl56
-rw-r--r--libs/input/tests/InputEvent_test.cpp309
-rw-r--r--libs/input/tests/InputPublisherAndConsumer_test.cpp145
-rw-r--r--libs/input/tests/StructLayout_test.cpp22
-rw-r--r--libs/input/tests/VelocityTracker_test.cpp5
-rw-r--r--libs/input/tests/VerifiedInputEvent_test.cpp4
-rw-r--r--libs/nativedisplay/AChoreographer.cpp209
-rw-r--r--libs/nativedisplay/ADisplay.cpp15
-rw-r--r--libs/nativedisplay/include-private/private/android/choreographer.h28
-rw-r--r--libs/nativedisplay/include/apex/display.h5
-rw-r--r--libs/nativedisplay/include/surfacetexture/ImageConsumer.h3
-rw-r--r--libs/nativedisplay/include/surfacetexture/SurfaceTexture.h5
-rw-r--r--libs/nativedisplay/include/surfacetexture/surface_texture_platform.h13
-rw-r--r--libs/nativedisplay/libnativedisplay.map.txt17
-rw-r--r--libs/nativedisplay/surfacetexture/ImageConsumer.cpp4
-rw-r--r--libs/nativedisplay/surfacetexture/SurfaceTexture.cpp12
-rw-r--r--libs/nativedisplay/surfacetexture/surface_texture.cpp20
-rw-r--r--libs/nativewindow/AHardwareBuffer.cpp10
-rw-r--r--libs/nativewindow/ANativeWindow.cpp37
-rw-r--r--libs/nativewindow/include/android/data_space.h407
-rw-r--r--libs/nativewindow/include/android/hardware_buffer.h8
-rw-r--r--libs/nativewindow/include/system/window.h18
-rw-r--r--libs/renderengine/Android.bp9
-rw-r--r--libs/renderengine/ExternalTexture.cpp20
-rw-r--r--libs/renderengine/RenderEngine.cpp29
-rw-r--r--libs/renderengine/benchmark/Android.bp59
-rw-r--r--libs/renderengine/benchmark/Codec.cpp136
-rw-r--r--libs/renderengine/benchmark/Flags.cpp48
-rw-r--r--libs/renderengine/benchmark/RenderEngineBench.cpp260
-rw-r--r--libs/renderengine/benchmark/RenderEngineBench.h65
-rw-r--r--libs/renderengine/benchmark/main.cpp32
-rw-r--r--libs/renderengine/benchmark/resources/homescreen.pngbin0 -> 4907729 bytes
-rw-r--r--libs/renderengine/gl/GLESRenderEngine.cpp114
-rw-r--r--libs/renderengine/gl/GLESRenderEngine.h10
-rw-r--r--libs/renderengine/include/renderengine/DisplaySettings.h16
-rw-r--r--libs/renderengine/include/renderengine/ExternalTexture.h32
-rw-r--r--libs/renderengine/include/renderengine/LayerSettings.h7
-rw-r--r--libs/renderengine/include/renderengine/RenderEngine.h40
-rw-r--r--libs/renderengine/include/renderengine/impl/ExternalTexture.h60
-rw-r--r--libs/renderengine/include/renderengine/mock/FakeExternalTexture.h51
-rw-r--r--libs/renderengine/include/renderengine/mock/RenderEngine.h13
-rw-r--r--libs/renderengine/skia/Cache.cpp98
-rw-r--r--libs/renderengine/skia/ColorSpaces.cpp21
-rw-r--r--libs/renderengine/skia/SkiaGLRenderEngine.cpp373
-rw-r--r--libs/renderengine/skia/SkiaGLRenderEngine.h30
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.h16
-rw-r--r--libs/renderengine/skia/filters/BlurFilter.cpp99
-rw-r--r--libs/renderengine/skia/filters/BlurFilter.h35
-rw-r--r--libs/renderengine/skia/filters/GaussianBlurFilter.cpp69
-rw-r--r--libs/renderengine/skia/filters/GaussianBlurFilter.h47
-rw-r--r--libs/renderengine/skia/filters/KawaseBlurFilter.cpp99
-rw-r--r--libs/renderengine/skia/filters/KawaseBlurFilter.h54
-rw-r--r--libs/renderengine/skia/filters/LinearEffect.cpp437
-rw-r--r--libs/renderengine/skia/filters/LinearEffect.h59
-rw-r--r--libs/renderengine/skia/filters/StretchShaderFactory.cpp2
-rw-r--r--libs/renderengine/tests/Android.bp7
-rw-r--r--libs/renderengine/tests/RenderEngineTest.cpp832
-rw-r--r--libs/renderengine/tests/RenderEngineThreadedTest.cpp37
-rw-r--r--libs/renderengine/threaded/RenderEngineThreaded.cpp59
-rw-r--r--libs/renderengine/threaded/RenderEngineThreaded.h16
-rw-r--r--libs/sensor/Sensor.cpp38
-rw-r--r--libs/sensor/include/sensor/Sensor.h9
-rw-r--r--libs/shaders/Android.bp44
-rw-r--r--libs/shaders/OWNERS4
-rw-r--r--libs/shaders/include/shaders/shaders.h105
-rw-r--r--libs/shaders/shaders.cpp497
-rw-r--r--libs/tonemap/Android.bp43
-rw-r--r--libs/tonemap/OWNERS4
-rw-r--r--libs/tonemap/TEST_MAPPING10
-rw-r--r--libs/tonemap/include/tonemap/tonemap.h119
-rw-r--r--libs/tonemap/tests/Android.bp39
-rw-r--r--libs/tonemap/tests/tonemap_test.cpp76
-rw-r--r--libs/tonemap/tonemap.cpp666
-rw-r--r--libs/ui/Android.bp16
-rw-r--r--libs/ui/DebugUtils.cpp2
-rw-r--r--libs/ui/DisplayIdentification.cpp402
-rw-r--r--libs/ui/Gralloc4.cpp107
-rw-r--r--libs/ui/GraphicBufferMapper.cpp5
-rw-r--r--libs/ui/PixelFormat.cpp21
-rw-r--r--libs/ui/StaticAsserts.cpp24
-rw-r--r--libs/ui/StaticDisplayInfo.cpp5
-rw-r--r--libs/ui/Transform.cpp5
-rw-r--r--libs/ui/include/ui/DisplayId.h35
-rw-r--r--libs/ui/include/ui/DisplayIdentification.h86
-rw-r--r--libs/ui/include/ui/DisplayState.h7
-rw-r--r--libs/ui/include/ui/Fence.h13
-rw-r--r--libs/ui/include/ui/Gralloc.h5
-rw-r--r--libs/ui/include/ui/Gralloc4.h5
-rw-r--r--libs/ui/include/ui/GraphicBufferMapper.h2
-rw-r--r--libs/ui/include/ui/LayerStack.h78
-rw-r--r--libs/ui/include/ui/PixelFormat.h2
-rw-r--r--libs/ui/include/ui/Size.h183
-rw-r--r--libs/ui/include/ui/StaticDisplayInfo.h2
-rw-r--r--libs/ui/include/ui/fuzzer/FuzzableDataspaces.h80
-rw-r--r--libs/ui/include_mock/ui/MockFence.h33
-rw-r--r--libs/ui/include_types/ui/DataspaceUtils.h29
l---------libs/ui/include_types/ui/GraphicTypes.h1
-rw-r--r--libs/ui/tests/Android.bp76
-rw-r--r--libs/ui/tests/DataspaceUtils_test.cpp53
-rw-r--r--libs/ui/tests/DisplayId_test.cpp12
-rw-r--r--libs/ui/tests/DisplayIdentification_test.cpp419
-rw-r--r--libs/ui/tests/MockFence_test.cpp57
-rw-r--r--libs/ui/tests/Size_test.cpp10
-rw-r--r--libs/ui/tests/Transform_test.cpp49
-rw-r--r--libs/vr/libvrflinger/Android.bp107
-rw-r--r--libs/vr/libvrflinger/acquired_buffer.cpp103
-rw-r--r--libs/vr/libvrflinger/acquired_buffer.h87
-rw-r--r--libs/vr/libvrflinger/display_manager_service.cpp142
-rw-r--r--libs/vr/libvrflinger/display_manager_service.h74
-rw-r--r--libs/vr/libvrflinger/display_service.cpp437
-rw-r--r--libs/vr/libvrflinger/display_service.h126
-rw-r--r--libs/vr/libvrflinger/display_surface.cpp488
-rw-r--r--libs/vr/libvrflinger/display_surface.h188
-rw-r--r--libs/vr/libvrflinger/epoll_event_dispatcher.cpp142
-rw-r--r--libs/vr/libvrflinger/epoll_event_dispatcher.h63
-rw-r--r--libs/vr/libvrflinger/hardware_composer.cpp1541
-rw-r--r--libs/vr/libvrflinger/hardware_composer.h577
-rw-r--r--libs/vr/libvrflinger/hwc_types.h307
-rw-r--r--libs/vr/libvrflinger/include/dvr/vr_flinger.h70
-rw-r--r--libs/vr/libvrflinger/tests/Android.bp47
-rw-r--r--libs/vr/libvrflinger/tests/vrflinger_test.cpp226
-rw-r--r--libs/vr/libvrflinger/tests/vrflinger_test.filter5
-rw-r--r--libs/vr/libvrflinger/vr_flinger.cpp141
194 files changed, 10417 insertions, 7884 deletions
diff --git a/libs/battery/Android.bp b/libs/battery/Android.bp
new file mode 100644
index 0000000000..c860324359
--- /dev/null
+++ b/libs/battery/Android.bp
@@ -0,0 +1,37 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library {
+ name: "libbattery",
+ srcs: [
+ "LongArrayMultiStateCounter.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+ export_include_dirs: ["."],
+}
+
+cc_test {
+ name: "libbattery_test",
+ srcs: [
+ "MultiStateCounterTest.cpp",
+ "LongArrayMultiStateCounterTest.cpp",
+ ],
+ static_libs: ["libbattery"],
+ shared_libs: [
+ "liblog",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+}
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
new file mode 100644
index 0000000000..125cfaffa4
--- /dev/null
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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 "LongArrayMultiStateCounter.h"
+#include <log/log.h>
+
+namespace android {
+namespace battery {
+
+template <>
+bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue,
+ const std::vector<uint64_t>& newValue,
+ std::vector<uint64_t>* outValue) const {
+ size_t size = previousValue.size();
+ if (newValue.size() != size) {
+ ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size);
+ return false;
+ }
+
+ bool is_delta_valid = true;
+ for (int i = size - 1; i >= 0; i--) {
+ if (newValue[i] >= previousValue[i]) {
+ (*outValue)[i] = newValue[i] - previousValue[i];
+ } else {
+ (*outValue)[i] = 0;
+ is_delta_valid = false;
+ }
+ }
+ return is_delta_valid;
+}
+
+template <>
+void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1,
+ const std::vector<uint64_t>& value2, const uint64_t numerator,
+ const uint64_t denominator) const {
+ if (numerator != denominator) {
+ for (int i = value2.size() - 1; i >= 0; i--) {
+ // The caller ensures that denominator != 0
+ (*value1)[i] += value2[i] * numerator / denominator;
+ }
+ } else {
+ for (int i = value2.size() - 1; i >= 0; i--) {
+ (*value1)[i] += value2[i];
+ }
+ }
+}
+
+template <>
+std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
+ std::stringstream s;
+ s << "{";
+ bool first = true;
+ for (uint64_t n : v) {
+ if (!first) {
+ s << ", ";
+ }
+ s << n;
+ first = false;
+ }
+ s << "}";
+ return s.str();
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h
new file mode 100644
index 0000000000..f3439f6a0c
--- /dev/null
+++ b/libs/battery/LongArrayMultiStateCounter.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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 <vector>
+#include "MultiStateCounter.h"
+
+namespace android {
+namespace battery {
+
+typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter;
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp
new file mode 100644
index 0000000000..e4e6b2a49f
--- /dev/null
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include "LongArrayMultiStateCounter.h"
+
+namespace android {
+namespace battery {
+
+class LongArrayMultiStateCounterTest : public testing::Test {};
+
+TEST_F(LongArrayMultiStateCounterTest, stateChange) {
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
+ testCounter.setState(1, 2000);
+ testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+
+ // Time was split in half between the two states, so the counts will be split 50:50 too
+ EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0));
+ EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1));
+}
+
+TEST_F(LongArrayMultiStateCounterTest, accumulation) {
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
+ testCounter.setState(1, 2000);
+ testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+ testCounter.setState(0, 4000);
+ testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000);
+
+ // The first delta is split 50:50:
+ // 0: {50, 100, 150, 200}
+ // 1: {50, 100, 150, 200}
+ // The second delta is split 4:1
+ // 0: {80, 80, 80, 80}
+ // 1: {20, 20, 20, 20}
+ EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0));
+ EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
+}
+
+TEST_F(LongArrayMultiStateCounterTest, toString) {
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
+ testCounter.setState(1, 2000);
+ testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+
+ EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
+ testCounter.toString().c_str());
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
new file mode 100644
index 0000000000..0caf005a9a
--- /dev/null
+++ b/libs/battery/MultiStateCounter.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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 <inttypes.h>
+#include <log/log.h>
+#include <time.h>
+#include <sstream>
+#include <string>
+
+/**
+ * An object that can track changes of some value over time, taking into account an additional
+ * dimension: the object's state. As the tracked value changes, the deltas are distributed
+ * among the object states in accordance with the time spent in those states.
+ */
+namespace android {
+namespace battery {
+
+typedef uint16_t state_t;
+
+template <class T>
+class MultiStateCounter {
+ uint16_t stateCount;
+ state_t currentState;
+ time_t lastStateChangeTimestamp;
+ T emptyValue;
+ T lastValue;
+ time_t lastUpdateTimestamp;
+ T deltaValue;
+ bool isEnabled;
+
+ struct State {
+ time_t timeInStateSinceUpdate;
+ T counter;
+ };
+
+ State* states;
+
+public:
+ MultiStateCounter(uint16_t stateCount, const T& emptyValue);
+
+ virtual ~MultiStateCounter();
+
+ void setEnabled(bool enabled, time_t timestamp);
+
+ void setState(state_t state, time_t timestamp);
+
+ void setValue(state_t state, const T& value);
+
+ /**
+ * Updates the value by distributing the delta from the previously set value
+ * among states according to their respective time-in-state.
+ * Returns the delta from the previously set value.
+ */
+ const T& updateValue(const T& value, time_t timestamp);
+
+ /**
+ * Updates the value by distributing the specified increment among states according
+ * to their respective time-in-state.
+ */
+ void incrementValue(const T& increment, time_t timestamp);
+
+ /**
+ * Adds the specified increment to the value for the current state, without affecting
+ * the last updated value or timestamp. Ignores partial time-in-state: the entirety of
+ * the increment is given to the current state.
+ */
+ void addValue(const T& increment);
+
+ void reset();
+
+ uint16_t getStateCount();
+
+ const T& getCount(state_t state);
+
+ std::string toString();
+
+private:
+ /**
+ * Subtracts previousValue from newValue and returns the result in outValue.
+ * Returns true iff the combination of previousValue and newValue is valid
+ * (newValue >= prevValue)
+ */
+ bool delta(const T& previousValue, const T& newValue, T* outValue) const;
+
+ /**
+ * Adds value2 to value1 and stores the result in value1. Denominator is
+ * guaranteed to be non-zero.
+ */
+ void add(T* value1, const T& value2, const uint64_t numerator,
+ const uint64_t denominator) const;
+
+ std::string valueToString(const T& value) const;
+};
+
+// ---------------------- MultiStateCounter Implementation -------------------------
+// Since MultiStateCounter is a template, the implementation must be inlined.
+
+template <class T>
+MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
+ : stateCount(stateCount),
+ currentState(0),
+ lastStateChangeTimestamp(-1),
+ emptyValue(emptyValue),
+ lastValue(emptyValue),
+ lastUpdateTimestamp(-1),
+ deltaValue(emptyValue),
+ isEnabled(true) {
+ states = new State[stateCount];
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ states[i].counter = emptyValue;
+ }
+}
+
+template <class T>
+MultiStateCounter<T>::~MultiStateCounter() {
+ delete[] states;
+};
+
+template <class T>
+void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
+ if (enabled == isEnabled) {
+ return;
+ }
+
+ if (!enabled) {
+ // Confirm the current state for the side-effect of updating the time-in-state
+ // counter for the current state.
+ setState(currentState, timestamp);
+ }
+
+ isEnabled = enabled;
+
+ if (lastStateChangeTimestamp >= 0) {
+ lastStateChangeTimestamp = timestamp;
+ }
+}
+
+template <class T>
+void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
+ if (isEnabled && lastStateChangeTimestamp >= 0) {
+ if (timestamp >= lastStateChangeTimestamp) {
+ states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
+ } else {
+ ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
+ // The accumulated durations have become unreliable. For example, if the timestamp
+ // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
+ // we would get 4000, which is greater than (last - first). This could lead to
+ // counts exceeding 100%.
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ }
+ currentState = state;
+ lastStateChangeTimestamp = timestamp;
+}
+
+template <class T>
+void MultiStateCounter<T>::setValue(state_t state, const T& value) {
+ states[state].counter = value;
+}
+
+template <class T>
+const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
+ T* returnValue = &emptyValue;
+
+ // If the counter is disabled, we ignore the update, except when the counter got disabled after
+ // the previous update, in which case we still need to pick up the residual delta.
+ if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
+ // Confirm the current state for the side-effect of updating the time-in-state
+ // counter for the current state.
+ setState(currentState, timestamp);
+
+ if (lastUpdateTimestamp >= 0) {
+ if (timestamp > lastUpdateTimestamp) {
+ if (delta(lastValue, value, &deltaValue)) {
+ returnValue = &deltaValue;
+ time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
+ for (int i = 0; i < stateCount; i++) {
+ time_t timeInState = states[i].timeInStateSinceUpdate;
+ if (timeInState) {
+ add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ } else {
+ std::stringstream str;
+ str << "updateValue is called with a value " << valueToString(value)
+ << ", which is lower than the previous value " << valueToString(lastValue)
+ << "\n";
+ ALOGE("%s", str.str().c_str());
+
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ } else if (timestamp < lastUpdateTimestamp) {
+ ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
+
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ }
+ }
+ lastValue = value;
+ lastUpdateTimestamp = timestamp;
+ return *returnValue;
+}
+
+template <class T>
+void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
+ T newValue = lastValue;
+ add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
+ updateValue(newValue, timestamp);
+}
+
+template <class T>
+void MultiStateCounter<T>::addValue(const T& value) {
+ if (!isEnabled) {
+ return;
+ }
+ add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
+}
+
+template <class T>
+void MultiStateCounter<T>::reset() {
+ lastStateChangeTimestamp = -1;
+ lastUpdateTimestamp = -1;
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ states[i].counter = emptyValue;
+ }
+}
+
+template <class T>
+uint16_t MultiStateCounter<T>::getStateCount() {
+ return stateCount;
+}
+
+template <class T>
+const T& MultiStateCounter<T>::getCount(state_t state) {
+ return states[state].counter;
+}
+
+template <class T>
+std::string MultiStateCounter<T>::toString() {
+ std::stringstream str;
+ str << "[";
+ for (int i = 0; i < stateCount; i++) {
+ if (i != 0) {
+ str << ", ";
+ }
+ str << i << ": " << valueToString(states[i].counter);
+ if (states[i].timeInStateSinceUpdate > 0) {
+ str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
+ }
+ }
+ str << "]";
+ if (lastUpdateTimestamp >= 0) {
+ str << " updated: " << lastUpdateTimestamp;
+ }
+ if (lastStateChangeTimestamp >= 0) {
+ str << " currentState: " << currentState;
+ if (lastStateChangeTimestamp > lastUpdateTimestamp) {
+ str << " stateChanged: " << lastStateChangeTimestamp;
+ }
+ } else {
+ str << " currentState: none";
+ }
+ if (!isEnabled) {
+ str << " disabled";
+ }
+ return str.str();
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
new file mode 100644
index 0000000000..cb11a5444d
--- /dev/null
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include "MultiStateCounter.h"
+
+namespace android {
+namespace battery {
+
+typedef MultiStateCounter<double> DoubleMultiStateCounter;
+
+template <>
+bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue,
+ double* outValue) const {
+ *outValue = newValue - previousValue;
+ return *outValue >= 0;
+}
+
+template <>
+void DoubleMultiStateCounter::add(double* value1, const double& value2, const uint64_t numerator,
+ const uint64_t denominator) const {
+ if (numerator != denominator) {
+ // The caller ensures that denominator != 0
+ *value1 += value2 * numerator / denominator;
+ } else {
+ *value1 += value2;
+ }
+}
+
+template <>
+std::string DoubleMultiStateCounter::valueToString(const double& v) const {
+ return std::to_string(v);
+}
+
+class MultiStateCounterTest : public testing::Test {};
+
+TEST_F(MultiStateCounterTest, constructor) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ double delta = testCounter.updateValue(3.14, 3000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+ EXPECT_DOUBLE_EQ(3.14, delta);
+}
+
+TEST_F(MultiStateCounterTest, stateChange) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setState(2, 1000);
+ testCounter.updateValue(6.0, 3000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, setEnabled) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setEnabled(false, 1000);
+ testCounter.setState(2, 2000);
+ testCounter.updateValue(6.0, 3000);
+
+ // In state 1: accumulated 1000 before disabled, that's 6.0 * 1000/3000 = 2.0
+ // In state 2: 0, since it is still disabled
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ // Should have no effect since the counter is disabled
+ testCounter.setState(0, 3500);
+
+ // Should have no effect since the counter is disabled
+ testCounter.updateValue(10.0, 4000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ testCounter.setState(2, 4500);
+
+ // Enable the counter to partially accumulate deltas for the current state, 2
+ testCounter.setEnabled(true, 5000);
+ testCounter.setEnabled(false, 6000);
+ testCounter.setEnabled(true, 7000);
+ testCounter.updateValue(20.0, 8000);
+
+ // The delta is 10.0 over 5000-3000=2000.
+ // Counter has been enabled in state 2 for (6000-5000)+(8000-7000) = 2000,
+ // so its share is (20.0-10.0) * 2000/(8000-4000) = 5.0
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(5.0, testCounter.getCount(2));
+
+ testCounter.reset();
+ testCounter.setState(0, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 2000);
+ testCounter.setEnabled(false, 3000);
+ testCounter.updateValue(200, 5000);
+
+ // 200 over 5000 = 40 per second
+ // Counter was in state 0 from 0 to 2000, so 2 sec, so the count should be 40 * 2 = 80
+ // It stayed in state 1 from 2000 to 3000, at which point the counter was disabled,
+ // so the count for state 1 should be 40 * 1 = 40.
+ // The remaining 2 seconds from 3000 to 5000 don't count because the counter was disabled.
+ EXPECT_DOUBLE_EQ(80.0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(40.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, reset) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.updateValue(2.72, 3000);
+
+ testCounter.reset();
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ // Assert that we can still continue accumulating after a reset
+ testCounter.updateValue(0, 4000);
+ testCounter.updateValue(3.14, 5000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, timeAdjustment_setState) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setState(2, 2000);
+
+ // Time moves back
+ testCounter.setState(1, 1000);
+ testCounter.updateValue(6.0, 3000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+
+ // We were in state 1 from 0 to 2000, which was erased because the time moved back.
+ // Then from 1000 to 3000, so we expect the count to be 6 * (2000/3000)
+ EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(1));
+
+ // No time was effectively accumulated for state 2, because the timestamp moved back
+ // while we were in state 2.
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) {
+ DoubleMultiStateCounter testCounter(1, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ // Time moves back. The delta over the negative interval from 2000 to 1000 is ignored
+ testCounter.updateValue(8.0, 1000);
+ double delta = testCounter.updateValue(11.0, 3000);
+
+ // The total accumulated count is:
+ // 6.0 // For the period 0-2000
+ // +(11.0-8.0) // For the period 1000-3000
+ EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+
+ // 11.0-8.0
+ EXPECT_DOUBLE_EQ(3.0, delta);
+}
+
+TEST_F(MultiStateCounterTest, updateValue_nonmonotonic) {
+ DoubleMultiStateCounter testCounter(2, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ // Value goes down. The negative delta from 6.0 to 4.0 is ignored
+ testCounter.updateValue(4.0, 3000);
+
+ // Value goes up again. The positive delta from 4.0 to 7.0 is accumulated.
+ double delta = testCounter.updateValue(7.0, 4000);
+
+ // The total accumulated count is:
+ // 6.0 // For the period 0-2000
+ // +(7.0-4.0) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+
+ // 7.0-4.0
+ EXPECT_DOUBLE_EQ(3.0, delta);
+}
+
+TEST_F(MultiStateCounterTest, incrementValue) {
+ DoubleMultiStateCounter testCounter(2, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ testCounter.setState(1, 3000);
+
+ testCounter.incrementValue(8.0, 6000);
+
+ // The total accumulated count is:
+ // 6.0 // For the period 0-2000
+ // +(8.0 * 0.25) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(8.0, testCounter.getCount(0));
+
+ // 0 // For the period 0-3000
+ // +(8.0 * 0.75) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(6.0, testCounter.getCount(1));
+}
+
+TEST_F(MultiStateCounterTest, addValue) {
+ DoubleMultiStateCounter testCounter(1, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ testCounter.addValue(8.0);
+
+ EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0));
+
+ testCounter.setEnabled(false, 3000);
+ testCounter.addValue(888.0);
+
+ EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0));
+}
+
+TEST_F(MultiStateCounterTest, toString) {
+ DoubleMultiStateCounter testCounter(2, 0);
+
+ EXPECT_STREQ("[0: 0.000000, 1: 0.000000] currentState: none", testCounter.toString().c_str());
+
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setState(1, 2000);
+ EXPECT_STREQ("[0: 0.000000, 1: 0.000000 timeInStateSinceUpdate: 2000]"
+ " updated: 0 currentState: 1 stateChanged: 2000",
+ testCounter.toString().c_str());
+
+ testCounter.updateValue(3.14, 3000);
+
+ EXPECT_STREQ("[0: 0.000000, 1: 3.140000] updated: 3000 currentState: 1",
+ testCounter.toString().c_str());
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index f295417ab2..706783093c 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -228,10 +228,8 @@ constexpr const char* const kManualInterfaces[] = {
"android.gfx.tests.IIPCTest",
"android.gfx.tests.ISafeInterfaceTest",
"android.graphicsenv.IGpuService",
- "android.gui.DisplayEventConnection",
"android.gui.IConsumerListener",
"android.gui.IGraphicBufferConsumer",
- "android.gui.IRegionSamplingListener",
"android.gui.ITransactionComposerListener",
"android.gui.SensorEventConnection",
"android.gui.SensorServer",
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 565542ba55..41638976cd 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -289,7 +289,7 @@ bool AIBinder_isAlive(const AIBinder* binder) __INTRODUCED_IN(29);
/**
* Built-in transaction for all binder objects. This sends a transaction that will immediately
* return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a
- * sanity check.
+ * consistency check.
*
* Available since API level 29.
*
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 2524c5f6d2..bc2eb23677 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -15,11 +15,14 @@ cc_test {
},
srcs: [
"Flags_test.cpp",
+ "cast_test.cpp",
+ "concat_test.cpp",
+ "enum_test.cpp",
"future_test.cpp",
- "NamedEnum_test.cpp",
"small_map_test.cpp",
"small_vector_test.cpp",
"static_vector_test.cpp",
+ "string_test.cpp",
],
cflags: [
"-Wall",
diff --git a/libs/ftl/Flags_test.cpp b/libs/ftl/Flags_test.cpp
index 8c00b5299b..d241fa272a 100644
--- a/libs/ftl/Flags_test.cpp
+++ b/libs/ftl/Flags_test.cpp
@@ -23,7 +23,7 @@ namespace android::test {
using namespace android::flag_operators;
-enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
+enum class TestFlags : uint8_t { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
TEST(Flags, Test) {
Flags<TestFlags> flags = TestFlags::ONE;
@@ -165,7 +165,7 @@ TEST(Flags, String_KnownValues) {
TEST(Flags, String_UnknownValues) {
auto flags = Flags<TestFlags>(0b1011);
- ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
+ ASSERT_EQ(flags.string(), "ONE | TWO | 0b1000");
}
TEST(FlagsIterator, IteratesOverAllFlags) {
@@ -210,18 +210,4 @@ TEST(FlagsIterator, PreFixIncrement) {
ASSERT_EQ(++iter, flags.end());
}
-TEST(FlagNames, RuntimeFlagName) {
- TestFlags f = TestFlags::ONE;
- ASSERT_EQ(flag_name(f), "ONE");
-}
-
-TEST(FlagNames, RuntimeUnknownFlagName) {
- TestFlags f = static_cast<TestFlags>(0x8);
- ASSERT_EQ(flag_name(f), std::nullopt);
-}
-
-TEST(FlagNames, CompileTimeFlagName) {
- static_assert(flag_name<TestFlags::TWO>() == "TWO");
-}
-
-} // namespace android::test \ No newline at end of file
+} // namespace android::test
diff --git a/libs/ftl/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp
deleted file mode 100644
index dff2b8aaa1..0000000000
--- a/libs/ftl/NamedEnum_test.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <ftl/NamedEnum.h>
-
-namespace android {
-
-// Test enum class maximum enum value smaller than default maximum of 8.
-enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 };
-// Big enum contains enum values greater than default maximum of 8.
-enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF };
-
-// Declared to specialize the maximum enum since the enum size exceeds 8 by default.
-template <>
-constexpr size_t NamedEnum::max<TestBigEnums> = 16;
-
-namespace test {
-using android::TestBigEnums;
-using android::TestEnums;
-
-TEST(NamedEnum, RuntimeNamedEnum) {
- TestEnums e = TestEnums::ZERO;
- ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
-
- e = TestEnums::ONE;
- ASSERT_EQ(NamedEnum::enum_name(e), "ONE");
-
- e = TestEnums::THREE;
- ASSERT_EQ(NamedEnum::enum_name(e), "THREE");
-
- e = TestEnums::SEVEN;
- ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN");
-}
-
-// Test big enum
-TEST(NamedEnum, RuntimeBigNamedEnum) {
- TestBigEnums e = TestBigEnums::ZERO;
- ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
-
- e = TestBigEnums::FIFTEEN;
- ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN");
-}
-
-TEST(NamedEnum, RuntimeNamedEnumAsString) {
- TestEnums e = TestEnums::ZERO;
- ASSERT_EQ(NamedEnum::string(e), "ZERO");
-
- e = TestEnums::ONE;
- ASSERT_EQ(NamedEnum::string(e), "ONE");
-
- e = TestEnums::THREE;
- ASSERT_EQ(NamedEnum::string(e), "THREE");
-
- e = TestEnums::SEVEN;
- ASSERT_EQ(NamedEnum::string(e), "SEVEN");
-}
-
-TEST(NamedEnum, RuntimeBigNamedEnumAsString) {
- TestBigEnums e = TestBigEnums::ZERO;
- ASSERT_EQ(NamedEnum::string(e), "ZERO");
-
- e = TestBigEnums::FIFTEEN;
- ASSERT_EQ(NamedEnum::string(e), "FIFTEEN");
-}
-
-TEST(NamedEnum, RuntimeUnknownNamedEnum) {
- TestEnums e = static_cast<TestEnums>(0x5);
- ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
- e = static_cast<TestEnums>(0x9);
- ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
-}
-
-TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
- TestEnums e = static_cast<TestEnums>(0x5);
- ASSERT_EQ(NamedEnum::string(e), "05");
- e = static_cast<TestEnums>(0x9);
- ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009");
-}
-
-TEST(NamedEnum, CompileTimeFlagName) {
- static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO");
- static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE");
-}
-
-} // namespace test
-
-} // namespace android
diff --git a/libs/ftl/cast_test.cpp b/libs/ftl/cast_test.cpp
new file mode 100644
index 0000000000..2abcb8fe66
--- /dev/null
+++ b/libs/ftl/cast_test.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/cast.h>
+#include <gtest/gtest.h>
+
+#include <cfloat>
+#include <cmath>
+#include <limits>
+
+namespace android::test {
+
+using ftl::cast_safety;
+using ftl::CastSafety;
+
+template <typename T>
+constexpr T min = std::numeric_limits<T>::lowest();
+
+template <typename T>
+constexpr T max = std::numeric_limits<T>::max();
+
+template <typename T>
+constexpr T inf = std::numeric_limits<T>::infinity();
+
+template <typename T>
+constexpr T NaN = std::numeric_limits<T>::quiet_NaN();
+
+// Keep in sync with example usage in header file.
+
+static_assert(cast_safety<uint8_t>(-1) == CastSafety::kUnderflow);
+static_assert(cast_safety<int8_t>(128u) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(static_cast<float>(INT32_MAX)) == CastSafety::kOverflow);
+
+static_assert(cast_safety<float>(-DBL_MAX) == CastSafety::kUnderflow);
+
+// Unsigned to unsigned.
+
+static_assert(cast_safety<uint8_t>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<uint8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint32_t>(max<uint64_t>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>) + 1) ==
+ CastSafety::kOverflow);
+
+// Unsigned to signed.
+
+static_assert(cast_safety<int16_t>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<int16_t>(max<uint8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<int16_t>(max<uint16_t>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) - 1) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) + 1) ==
+ CastSafety::kOverflow);
+
+// Signed to unsigned.
+
+static_assert(cast_safety<uint16_t>(0) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<int8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<int16_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint32_t>(-1) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(max<int64_t>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) - 1) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) + 1) ==
+ CastSafety::kOverflow);
+
+// Signed to signed.
+
+static_assert(cast_safety<int8_t>(-129) == CastSafety::kUnderflow);
+static_assert(cast_safety<int8_t>(-128) == CastSafety::kSafe);
+static_assert(cast_safety<int8_t>(127) == CastSafety::kSafe);
+static_assert(cast_safety<int8_t>(128) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int32_t>(static_cast<int64_t>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<int64_t>(max<int32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<int16_t>(min<int32_t>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(max<int64_t>) == CastSafety::kOverflow);
+
+// Float to float.
+
+static_assert(cast_safety<double>(max<float>) == CastSafety::kSafe);
+static_assert(cast_safety<double>(min<float>) == CastSafety::kSafe);
+
+static_assert(cast_safety<float>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<float>(max<double>) == CastSafety::kOverflow);
+
+TEST(CastSafety, FloatToFloat) {
+ EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(min<float>), min<double>)),
+ CastSafety::kUnderflow);
+ EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(max<float>), max<double>)),
+ CastSafety::kOverflow);
+}
+
+// Unsigned to float.
+
+static_assert(cast_safety<float>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<float>(max<uint64_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<double>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<double>(max<uint64_t>) == CastSafety::kSafe);
+
+// Signed to float.
+
+static_assert(cast_safety<float>(min<int64_t>) == CastSafety::kSafe);
+static_assert(cast_safety<float>(max<int64_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<double>(min<int64_t>) == CastSafety::kSafe);
+static_assert(cast_safety<double>(max<int64_t>) == CastSafety::kSafe);
+
+// Float to unsigned.
+
+static_assert(cast_safety<uint32_t>(0.f) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(min<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(max<float>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow);
+
+static_assert(cast_safety<uint16_t>(-inf<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(inf<float>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(NaN<float>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<float>(max<uint32_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<uint32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<double>(max<uint32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint64_t>(0.0) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint64_t>(max<double>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(-.1) == CastSafety::kUnderflow);
+
+static_assert(cast_safety<uint64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(static_cast<float>(max<uint64_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(static_cast<double>(max<uint64_t>)) == CastSafety::kOverflow);
+
+// Float to signed.
+
+static_assert(cast_safety<int32_t>(0.f) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(min<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(max<float>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int16_t>(-inf<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(inf<double>) == CastSafety::kOverflow);
+static_assert(cast_safety<int64_t>(NaN<double>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int32_t>(static_cast<float>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<int32_t>(static_cast<double>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<int64_t>(0.0) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int64_t>(max<double>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int64_t>(static_cast<float>(min<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<int64_t>(static_cast<double>(min<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kOverflow);
+
+TEST(CastSafety, FloatToSigned) {
+ constexpr int32_t kMax = ftl::details::safe_limits<int32_t, float>::max();
+ static_assert(kMax == 2'147'483'520);
+ EXPECT_EQ(kMax, static_cast<int32_t>(std::nexttowardf(max<int32_t>, 0)));
+
+ EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, 0)), CastSafety::kSafe);
+ EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, 0)), CastSafety::kSafe);
+ EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, 0)), CastSafety::kSafe);
+ EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, 0)), CastSafety::kSafe);
+
+ EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, min<float>)),
+ CastSafety::kUnderflow);
+ EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, max<float>)),
+ CastSafety::kOverflow);
+ EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, min<double>)),
+ CastSafety::kUnderflow);
+ EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, max<double>)),
+ CastSafety::kOverflow);
+}
+
+} // namespace android::test
diff --git a/libs/ftl/concat_test.cpp b/libs/ftl/concat_test.cpp
new file mode 100644
index 0000000000..8ecb1b252d
--- /dev/null
+++ b/libs/ftl/concat_test.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/concat.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(Concat, Example) {
+ std::string_view name = "Volume";
+ ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB");
+
+ EXPECT_EQ(string.str(), "Vol: -3 dB");
+ EXPECT_EQ(string.c_str()[string.size()], '\0');
+}
+
+namespace {
+
+static_assert(ftl::Concat{"foo"}.str() == "foo");
+static_assert(ftl::Concat{ftl::truncated<3>("foobar")}.str() == "foo");
+
+constexpr ftl::Concat kConcat{"po", "trz", "ebie"};
+
+static_assert(kConcat.size() == 9);
+static_assert(kConcat.max_size() == 9);
+static_assert(kConcat.str() == "potrzebie");
+static_assert(kConcat.str() == std::string_view(kConcat.c_str()));
+
+constexpr auto concat() {
+ return ftl::Concat{ftl::truncated<1>("v???"), ftl::truncated<2>("ee??"),
+ ftl::truncated<3>("ble?"), ftl::truncated<4>("fetz"),
+ ftl::truncated<90>("er")};
+}
+
+static_assert(concat().size() == 12);
+static_assert(concat().max_size() == 100);
+static_assert(concat().str() == "veeblefetzer");
+static_assert(concat().str() == std::string_view(concat().c_str()));
+
+} // namespace
+} // namespace android::test
diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp
new file mode 100644
index 0000000000..d8ce7a5e7b
--- /dev/null
+++ b/libs/ftl/enum_test.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+namespace {
+
+enum class E { A, B, C, F = 5, ftl_last = F };
+
+static_assert(ftl::enum_begin_v<E> == E::A);
+static_assert(ftl::enum_last_v<E> == E::F);
+static_assert(ftl::enum_size_v<E> == 6);
+
+static_assert(ftl::enum_name<E::B>() == "B");
+static_assert(ftl::enum_name<E::ftl_last>() == "F");
+static_assert(ftl::enum_name(E::C).value_or("?") == "C");
+static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
+
+enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+
+static_assert(ftl::enum_begin_v<F> == F{0});
+static_assert(ftl::enum_last_v<F> == F{15});
+static_assert(ftl::enum_size_v<F> == 16);
+
+static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
+static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
+
+// If a scoped enum is unsigned, its implicit range corresponds to its bit indices.
+enum class Flags : std::uint8_t {
+ kNone = 0,
+ kFlag1 = 0b0000'0010,
+ kFlag4 = 0b0001'0000,
+ kFlag7 = 0b1000'0000,
+ kMask = kFlag1 | kFlag4 | kFlag7,
+ kAll = 0b1111'1111
+};
+
+static_assert(ftl::enum_begin_v<Flags> == Flags{0});
+static_assert(ftl::enum_last_v<Flags> == Flags{7});
+static_assert(ftl::enum_size_v<Flags> == 8);
+
+static_assert(ftl::enum_name<Flags::kNone>() == "kNone");
+static_assert(ftl::enum_name<Flags::kFlag4>() == "kFlag4");
+static_assert(ftl::enum_name<Flags::kFlag7>() == "kFlag7");
+
+// Though not flags, the enumerators are within the implicit range of bit indices.
+enum class Planet : std::uint8_t {
+ kMercury,
+ kVenus,
+ kEarth,
+ kMars,
+ kJupiter,
+ kSaturn,
+ kUranus,
+ kNeptune
+};
+
+constexpr Planet kPluto{ftl::to_underlying(Planet::kNeptune) + 1}; // Honorable mention.
+
+static_assert(ftl::enum_begin_v<Planet> == Planet::kMercury);
+static_assert(ftl::enum_last_v<Planet> == Planet::kNeptune);
+static_assert(ftl::enum_size_v<Planet> == 8);
+
+static_assert(ftl::enum_name<Planet::kMercury>() == "kMercury");
+static_assert(ftl::enum_name<Planet::kSaturn>() == "kSaturn");
+
+// Unscoped enum must define explicit range, even if the underlying type is fixed.
+enum Temperature : int {
+ kRoom = 20,
+ kFridge = 4,
+ kFreezer = -18,
+
+ ftl_first = kFreezer,
+ ftl_last = kRoom
+};
+
+static_assert(ftl::enum_begin_v<Temperature> == kFreezer);
+static_assert(ftl::enum_last_v<Temperature> == kRoom);
+static_assert(ftl::enum_size_v<Temperature> == 39);
+
+static_assert(ftl::enum_name<kFreezer>() == "kFreezer");
+static_assert(ftl::enum_name<kFridge>() == "kFridge");
+static_assert(ftl::enum_name<kRoom>() == "kRoom");
+
+} // namespace
+
+TEST(Enum, Range) {
+ std::string string;
+ for (E v : ftl::enum_range<E>()) {
+ string += ftl::enum_name(v).value_or("?");
+ }
+ EXPECT_EQ(string, "ABC??F");
+}
+
+TEST(Enum, Name) {
+ {
+ EXPECT_EQ(ftl::flag_name(Flags::kFlag1), "kFlag1");
+ EXPECT_EQ(ftl::flag_name(Flags::kFlag7), "kFlag7");
+
+ EXPECT_EQ(ftl::flag_name(Flags::kNone), std::nullopt);
+ EXPECT_EQ(ftl::flag_name(Flags::kMask), std::nullopt);
+ EXPECT_EQ(ftl::flag_name(Flags::kAll), std::nullopt);
+ }
+ {
+ EXPECT_EQ(ftl::enum_name(Planet::kEarth), "kEarth");
+ EXPECT_EQ(ftl::enum_name(Planet::kNeptune), "kNeptune");
+
+ EXPECT_EQ(ftl::enum_name(kPluto), std::nullopt);
+ }
+ {
+ EXPECT_EQ(ftl::enum_name(kRoom), "kRoom");
+ EXPECT_EQ(ftl::enum_name(kFridge), "kFridge");
+ EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer");
+
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(-30)), std::nullopt);
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(0)), std::nullopt);
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(100)), std::nullopt);
+ }
+}
+
+TEST(Enum, String) {
+ {
+ EXPECT_EQ(ftl::flag_string(Flags::kFlag1), "kFlag1");
+ EXPECT_EQ(ftl::flag_string(Flags::kFlag7), "kFlag7");
+
+ EXPECT_EQ(ftl::flag_string(Flags::kNone), "0b0");
+ EXPECT_EQ(ftl::flag_string(Flags::kMask), "0b10010010");
+ EXPECT_EQ(ftl::flag_string(Flags::kAll), "0b11111111");
+ }
+ {
+ EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth");
+ EXPECT_EQ(ftl::enum_string(Planet::kNeptune), "kNeptune");
+
+ EXPECT_EQ(ftl::enum_string(kPluto), "8");
+ }
+ {
+ EXPECT_EQ(ftl::enum_string(kRoom), "kRoom");
+ EXPECT_EQ(ftl::enum_string(kFridge), "kFridge");
+ EXPECT_EQ(ftl::enum_string(kFreezer), "kFreezer");
+
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(-30)), "-30");
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(0)), "0");
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(100)), "100");
+ }
+}
+
+} // namespace android::test
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
index 323b9f91e7..ee650e5627 100644
--- a/libs/ftl/small_map_test.cpp
+++ b/libs/ftl/small_map_test.cpp
@@ -18,6 +18,9 @@
#include <gtest/gtest.h>
#include <cctype>
+#include <string>
+
+using namespace std::string_literals;
namespace android::test {
@@ -35,16 +38,19 @@ TEST(SmallMap, Example) {
EXPECT_TRUE(map.contains(123));
- EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
+ EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u);
- const auto opt = map.find(-1);
+ const auto opt = map.get(-1);
ASSERT_TRUE(opt);
std::string& ref = *opt;
EXPECT_TRUE(ref.empty());
ref = "xyz";
- EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+ map.emplace_or_replace(0, "vanilla", 2u, 3u);
+ EXPECT_TRUE(map.dynamic());
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
}
TEST(SmallMap, Construct) {
@@ -90,42 +96,290 @@ TEST(SmallMap, Construct) {
}
}
+TEST(SmallMap, UniqueKeys) {
+ {
+ // Duplicate mappings are discarded.
+ const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1);
+
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_EQ(map.max_size(), 9u);
+
+ using Map = decltype(map);
+ EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f)));
+ }
+ {
+ // Duplicate mappings may be reordered.
+ const SmallMap map = ftl::init::map('a', 'A')(
+ 'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F');
+
+ EXPECT_EQ(map.size(), 6u);
+ EXPECT_EQ(map.max_size(), 12u);
+
+ using Map = decltype(map);
+ EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F')));
+ }
+}
+
TEST(SmallMap, Find) {
{
// Constant reference.
- const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+ const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
- const auto opt = map.find('b');
+ const auto opt = map.get('b');
EXPECT_EQ(opt, 'B');
const char d = 'D';
- const auto ref = map.find('d').value_or(std::cref(d));
+ const auto ref = map.get('d').value_or(std::cref(d));
EXPECT_EQ(ref.get(), 'D');
}
{
// Mutable reference.
- ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+ SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
- const auto opt = map.find('c');
+ const auto opt = map.get('c');
EXPECT_EQ(opt, 'C');
char d = 'd';
- const auto ref = map.find('d').value_or(std::ref(d));
+ const auto ref = map.get('d').value_or(std::ref(d));
ref.get() = 'D';
EXPECT_EQ(d, 'D');
}
{
// Constant unary operation.
- const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
- EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
+ const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z');
}
{
// Mutable unary operation.
- ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
- EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
+ SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); }));
EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
}
}
+TEST(SmallMap, TryEmplace) {
+ SmallMap<int, std::string, 3> map;
+ using Pair = decltype(map)::value_type;
+
+ {
+ const auto [it, ok] = map.try_emplace(123, "abc");
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(*it, Pair(123, "abc"s));
+ }
+ {
+ const auto [it, ok] = map.try_emplace(42, 3u, '?');
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(*it, Pair(42, "???"s));
+ }
+ {
+ const auto [it, ok] = map.try_emplace(-1);
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(*it, Pair(-1, std::string()));
+ EXPECT_FALSE(map.dynamic());
+ }
+ {
+ // Insertion fails if mapping exists.
+ const auto [it, ok] = map.try_emplace(42, "!!!");
+ EXPECT_FALSE(ok);
+ EXPECT_EQ(*it, Pair(42, "???"));
+ EXPECT_FALSE(map.dynamic());
+ }
+ {
+ // Insertion at capacity promotes the map.
+ const auto [it, ok] = map.try_emplace(999, "xyz");
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(*it, Pair(999, "xyz"));
+ EXPECT_TRUE(map.dynamic());
+ }
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s)));
+}
+
+namespace {
+
+// The mapped type does not require a copy/move assignment operator.
+struct String {
+ template <typename... Args>
+ String(Args... args) : str(args...) {}
+ const std::string str;
+
+ bool operator==(const String& other) const { return other.str == str; }
+};
+
+} // namespace
+
+TEST(SmallMap, TryReplace) {
+ SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
+ using Pair = decltype(map)::value_type;
+
+ {
+ // Replacing fails unless mapping exists.
+ const auto it = map.try_replace(3, "c");
+ EXPECT_EQ(it, map.end());
+ }
+ {
+ // Replacement arguments can refer to the replaced mapping.
+ const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+ ASSERT_TRUE(ref);
+
+ // Construct std::string from one character.
+ const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
+ ASSERT_NE(it, map.end());
+ EXPECT_EQ(*it, Pair(2, "b"));
+ }
+
+ EXPECT_FALSE(map.dynamic());
+ EXPECT_TRUE(map.try_emplace(3, "abc").second);
+ EXPECT_TRUE(map.try_emplace(4, "d").second);
+ EXPECT_TRUE(map.dynamic());
+
+ {
+ // Replacing fails unless mapping exists.
+ const auto it = map.try_replace(5, "e");
+ EXPECT_EQ(it, map.end());
+ }
+ {
+ // Replacement arguments can refer to the replaced mapping.
+ const auto ref = map.get(3);
+ ASSERT_TRUE(ref);
+
+ // Construct std::string from substring.
+ const auto it = map.try_replace(3, ref->get().str, 2u, 1u);
+ ASSERT_NE(it, map.end());
+ EXPECT_EQ(*it, Pair(3, "c"));
+ }
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
+}
+
+TEST(SmallMap, EmplaceOrReplace) {
+ SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
+ using Pair = decltype(map)::value_type;
+
+ {
+ // New mapping is emplaced.
+ const auto [it, emplace] = map.emplace_or_replace(3, "c");
+ EXPECT_TRUE(emplace);
+ EXPECT_EQ(*it, Pair(3, "c"));
+ }
+ {
+ // Replacement arguments can refer to the replaced mapping.
+ const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+ ASSERT_TRUE(ref);
+
+ // Construct std::string from one character.
+ const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
+ EXPECT_FALSE(emplace);
+ EXPECT_EQ(*it, Pair(2, "b"));
+ }
+
+ EXPECT_FALSE(map.dynamic());
+ EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace.
+ EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace.
+ EXPECT_TRUE(map.dynamic());
+
+ {
+ // New mapping is emplaced.
+ const auto [it, emplace] = map.emplace_or_replace(5, "e");
+ EXPECT_TRUE(emplace);
+ EXPECT_EQ(*it, Pair(5, "e"));
+ }
+ {
+ // Replacement arguments can refer to the replaced mapping.
+ const auto ref = map.get(3);
+ ASSERT_TRUE(ref);
+
+ // Construct std::string from substring.
+ const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u);
+ EXPECT_FALSE(emplace);
+ EXPECT_EQ(*it, Pair(3, "c"));
+ }
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
+}
+
+TEST(SmallMap, Erase) {
+ {
+ SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4');
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_FALSE(map.erase(0)); // Key not found.
+
+ EXPECT_TRUE(map.erase(2));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
+
+ EXPECT_TRUE(map.erase(1));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
+
+ EXPECT_TRUE(map.erase(4));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
+
+ EXPECT_TRUE(map.erase(3));
+ EXPECT_FALSE(map.erase(3)); // Key not found.
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+ }
+ {
+ SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
+ map.try_emplace(4, '4');
+ EXPECT_TRUE(map.dynamic());
+
+ EXPECT_FALSE(map.erase(0)); // Key not found.
+
+ EXPECT_TRUE(map.erase(2));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
+
+ EXPECT_TRUE(map.erase(1));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
+
+ EXPECT_TRUE(map.erase(4));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
+
+ EXPECT_TRUE(map.erase(3));
+ EXPECT_FALSE(map.erase(3)); // Key not found.
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_TRUE(map.dynamic());
+ }
+}
+
+TEST(SmallMap, Clear) {
+ SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
+
+ map.clear();
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+
+ map = ftl::init::map(1, '1')(2, '2')(3, '3');
+ map.try_emplace(4, '4');
+
+ map.clear();
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_TRUE(map.dynamic());
+}
+
+TEST(SmallMap, KeyEqual) {
+ struct KeyEqual {
+ bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; }
+ };
+
+ SmallMap<int, char, 1, KeyEqual> map;
+
+ EXPECT_TRUE(map.try_emplace(3, '3').second);
+ EXPECT_FALSE(map.try_emplace(13, '3').second);
+
+ EXPECT_TRUE(map.try_emplace(22, '2').second);
+ EXPECT_TRUE(map.contains(42));
+
+ EXPECT_TRUE(map.try_emplace(111, '1').second);
+ EXPECT_EQ(map.get(321), '1');
+
+ map.erase(123);
+ EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2')));
+}
+
} // namespace android::test
diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp
index 3a03e696d1..42374969f1 100644
--- a/libs/ftl/small_vector_test.cpp
+++ b/libs/ftl/small_vector_test.cpp
@@ -460,4 +460,34 @@ TEST(SmallVector, Destroy) {
EXPECT_EQ(0, dead);
}
+TEST(SmallVector, Clear) {
+ int live = 0;
+ int dead = 0;
+
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_FALSE(counts.dynamic());
+
+ EXPECT_EQ(2, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_TRUE(counts.dynamic());
+
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(2, dead);
+}
+
} // namespace android::test
diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp
index cbe8dff527..2de3ad273e 100644
--- a/libs/ftl/static_vector_test.cpp
+++ b/libs/ftl/static_vector_test.cpp
@@ -396,4 +396,19 @@ TEST(StaticVector, Destroy) {
EXPECT_EQ(0, dead);
}
+TEST(StaticVector, Clear) {
+ int live = 0;
+ int dead = 0;
+
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_EQ(2, live);
+ EXPECT_EQ(0, dead);
+}
+
} // namespace android::test
diff --git a/libs/ftl/string_test.cpp b/libs/ftl/string_test.cpp
new file mode 100644
index 0000000000..f3d85c8319
--- /dev/null
+++ b/libs/ftl/string_test.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/string.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <limits>
+#include <sstream>
+#include <type_traits>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(String, ToChars) {
+ ftl::to_chars_buffer_t<> buffer;
+
+ EXPECT_EQ(ftl::to_chars(buffer, 123u), "123");
+ EXPECT_EQ(ftl::to_chars(buffer, -42, ftl::Radix::kBin), "-0b101010");
+ EXPECT_EQ(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex), "0xcafe");
+ EXPECT_EQ(ftl::to_chars(buffer, '*', ftl::Radix::kHex), "0x2a");
+}
+
+namespace {
+
+template <typename F, typename T>
+void ToCharsTest() {
+ constexpr auto kRadix = F::kRadix;
+
+ using Limits = std::numeric_limits<T>;
+ constexpr auto kMin = Limits::min();
+ constexpr auto kMax = Limits::max();
+ constexpr auto kNeg = static_cast<T>(-42);
+ constexpr auto kPos = static_cast<T>(123);
+
+ ftl::to_chars_buffer_t<T> buffer;
+
+ EXPECT_EQ(ftl::to_chars(buffer, kMin, kRadix), F{}(kMin));
+ EXPECT_EQ(ftl::to_chars(buffer, kMax, kRadix), F{}(kMax));
+ EXPECT_EQ(ftl::to_chars(buffer, kNeg, kRadix), F{}(kNeg));
+ EXPECT_EQ(ftl::to_chars(buffer, kPos, kRadix), F{}(kPos));
+}
+
+template <typename...>
+struct Types {};
+
+template <typename F, typename Types>
+struct ToCharsTests;
+
+template <typename F, typename T, typename... Ts>
+struct ToCharsTests<F, Types<T, Ts...>> {
+ static void test() {
+ ToCharsTest<F, T>();
+ ToCharsTests<F, Types<Ts...>>::test();
+ }
+};
+
+template <typename F>
+struct ToCharsTests<F, Types<>> {
+ static void test() {}
+};
+
+template <typename T, typename U = std::make_unsigned_t<T>>
+U to_unsigned(std::ostream& stream, T v) {
+ if (std::is_same_v<T, U>) return v;
+
+ if (v < 0) {
+ stream << '-';
+ return std::numeric_limits<U>::max() - static_cast<U>(v) + 1;
+ } else {
+ return static_cast<U>(v);
+ }
+}
+
+struct Bin {
+ static constexpr auto kRadix = ftl::Radix::kBin;
+
+ template <typename T>
+ std::string operator()(T v) const {
+ std::ostringstream stream;
+ auto u = to_unsigned(stream, v);
+ stream << "0b";
+
+ if (u == 0) {
+ stream << 0;
+ } else {
+ std::ostringstream digits;
+ do {
+ digits << (u & 1);
+ } while (u >>= 1);
+
+ const auto str = digits.str();
+ std::copy(str.rbegin(), str.rend(), std::ostream_iterator<char>(stream));
+ }
+
+ return stream.str();
+ }
+};
+
+struct Dec {
+ static constexpr auto kRadix = ftl::Radix::kDec;
+
+ template <typename T>
+ std::string operator()(T v) const {
+ return std::to_string(v);
+ }
+};
+
+struct Hex {
+ static constexpr auto kRadix = ftl::Radix::kHex;
+
+ template <typename T>
+ std::string operator()(T v) const {
+ std::ostringstream stream;
+ const auto u = to_unsigned(stream, v);
+ stream << "0x" << std::hex << std::nouppercase;
+ stream << (sizeof(T) == 1 ? static_cast<unsigned>(u) : u);
+ return stream.str();
+ }
+};
+
+using IntegerTypes =
+ Types<char, unsigned char, signed char, std::uint8_t, std::uint16_t, std::uint32_t,
+ std::uint64_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t>;
+
+} // namespace
+
+TEST(String, ToCharsBin) {
+ ToCharsTests<Bin, IntegerTypes>::test();
+
+ {
+ const std::uint8_t x = 0b1111'1111;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "0b11111111");
+ }
+ {
+ const std::int16_t x = -0b1000'0000'0000'0000;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "-0b1000000000000000");
+ }
+}
+
+TEST(String, ToCharsDec) {
+ ToCharsTests<Dec, IntegerTypes>::test();
+
+ {
+ const std::uint32_t x = UINT32_MAX;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x), "4294967295");
+ }
+ {
+ const std::int32_t x = INT32_MIN;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x), "-2147483648");
+ }
+}
+
+TEST(String, ToCharsHex) {
+ ToCharsTests<Hex, IntegerTypes>::test();
+
+ {
+ const std::uint16_t x = 0xfade;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kHex), "0xfade");
+ }
+ {
+ ftl::to_chars_buffer_t<> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, INT64_MIN, ftl::Radix::kHex), "-0x8000000000000000");
+ }
+}
+
+} // namespace android::test
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index cda9e19c1e..6afd1729de 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -33,7 +33,7 @@ cc_library {
target: {
darwin: {
enabled: false,
- }
+ },
},
vendor_available: true,
@@ -48,18 +48,18 @@ cc_library {
min_sdk_version: "29",
srcs: [
- "Gralloc4.cpp"
+ "Gralloc4.cpp",
],
shared_libs: [
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
"liblog",
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index e2f072a7ab..61e6657621 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -1134,6 +1134,18 @@ status_t decodeSmpte2094_40(const hidl_vec<uint8_t>& smpte2094_40,
decodeByteVector);
}
+status_t encodeSmpte2094_10(const std::optional<std::vector<uint8_t>>& smpte2094_10,
+ hidl_vec<uint8_t>* outSmpte2094_10) {
+ return encodeOptionalMetadata(MetadataType_Smpte2094_10, smpte2094_10, outSmpte2094_10,
+ encodeByteVector);
+}
+
+status_t decodeSmpte2094_10(const hidl_vec<uint8_t>& smpte2094_10,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_10) {
+ return decodeOptionalMetadata(MetadataType_Smpte2094_10, smpte2094_10, outSmpte2094_10,
+ decodeByteVector);
+}
+
status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
hidl_vec<uint8_t>* output) {
return encodeMetadata(metadataType, input, output, encodeInteger);
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 2f418acca5..deaffade1b 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -134,6 +134,12 @@ static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
aidl::android::hardware::graphics::common::
StandardMetadataType::SMPTE2094_40)};
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Smpte2094_10 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ StandardMetadataType::SMPTE2094_10)};
+
/*---------------------------------------------------------------------------------------------*/
/**
@@ -327,6 +333,11 @@ status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094
status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+status_t encodeSmpte2094_10(const std::optional<std::vector<uint8_t>>& smpte2094_10,
+ android::hardware::hidl_vec<uint8_t>* outSmpte2094_10);
+status_t decodeSmpte2094_10(const android::hardware::hidl_vec<uint8_t>& smpte2094_10,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_10);
+
/**
* The functions below can be used to encode and decode vendor metadata types.
*/
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
index 89cbf4ac4a..94e344f584 100644
--- a/libs/gralloc/types/tests/Gralloc4_test.cpp
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -455,6 +455,32 @@ TEST_P(Gralloc4TestSmpte2094_40, Smpte2094_40) {
ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
}
+class Gralloc4TestSmpte2094_10
+ : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> {};
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestSmpte2094_10Params, Gralloc4TestSmpte2094_10,
+ ::testing::Values(
+ std::optional<std::vector<uint8_t>>({}),
+ std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+ std::numeric_limits<uint8_t>::min() + 1,
+ std::numeric_limits<uint8_t>::min() + 2,
+ std::numeric_limits<uint8_t>::min() + 3,
+ std::numeric_limits<uint8_t>::min() + 4}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint8_t>::max() - 1,
+ std::numeric_limits<uint8_t>::max() - 2,
+ std::numeric_limits<uint8_t>::max() - 3,
+ std::numeric_limits<uint8_t>::max() - 4}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_10, Smpte2094_10) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(),
+ gralloc4::encodeSmpte2094_10,
+ gralloc4::decodeSmpte2094_10));
+}
+
class Gralloc4TestBufferDescriptorInfo : public testing::TestWithParam<BufferDescriptorInfo> { };
INSTANTIATE_TEST_CASE_P(
@@ -491,6 +517,7 @@ TEST_F(Gralloc4TestErrors, Gralloc4TestEncodeNull) {
ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_10({{}}, nullptr));
}
TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
@@ -516,6 +543,7 @@ TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_10(vec, nullptr));
}
TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index bd9abc1996..ec3587b79a 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -55,7 +55,7 @@ cc_library_headers {
filegroup {
name: "guiconstants_aidl",
srcs: [
- "android/**/DropInputMode.aidl",
+ "android/gui/DropInputMode.aidl",
"android/**/TouchOcclusionMode.aidl",
],
}
@@ -66,11 +66,14 @@ cc_library_static {
host_supported: true,
srcs: [
":guiconstants_aidl",
+ ":inputconstants_aidl",
+ "android/gui/DisplayInfo.aidl",
"android/gui/FocusRequest.aidl",
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
"android/gui/WindowInfo.aidl",
+ "DisplayInfo.cpp",
"WindowInfo.cpp",
],
@@ -91,7 +94,7 @@ cc_library_static {
],
aidl: {
- export_aidl_headers: true
+ export_aidl_headers: true,
},
include_dirs: [
@@ -178,11 +181,9 @@ cc_library_shared {
"FrameTimelineInfo.cpp",
"GLConsumer.cpp",
"IConsumerListener.cpp",
- "IDisplayEventConnection.cpp",
"IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
- "IRegionSamplingListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"ITransactionCompletedListener.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index f034642681..dd966837f4 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,7 +132,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name)
mSize(1, 1),
mRequestedSize(mSize),
mFormat(PIXEL_FORMAT_RGBA_8888),
- mNextTransaction(nullptr) {
+ mSyncTransaction(nullptr) {
createBufferQueue(&mProducer, &mConsumer);
// since the adapter is in the client process, set dequeue timeout
// explicitly so that dequeueBuffer will block
@@ -155,6 +155,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name)
ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
+ mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
mNumAcquired = 0;
mNumFrameAvailable = 0;
BQA_LOGV("BLASTBufferQueue created");
@@ -173,14 +174,14 @@ BLASTBufferQueue::~BLASTBufferQueue() {
BQA_LOGE("Applying pending transactions on dtor %d",
static_cast<uint32_t>(mPendingTransactions.size()));
SurfaceComposerClient::Transaction t;
- for (auto& [targetFrameNumber, transaction] : mPendingTransactions) {
- t.merge(std::move(transaction));
- }
- t.apply();
+ mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
+ t.setApplyToken(mApplyToken).apply();
}
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
int32_t format, SurfaceComposerClient::Transaction* outTransaction) {
+ LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL");
+
std::unique_lock _lock{mMutex};
if (mFormat != format) {
mFormat = format;
@@ -188,21 +189,22 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width,
}
SurfaceComposerClient::Transaction t;
- const bool setBackpressureFlag = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
+ const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
+ if (surfaceControlChanged && mSurfaceControl != nullptr) {
+ BQA_LOGD("Updating SurfaceControl without recreating BBQ");
+ }
bool applyTransaction = false;
// Always update the native object even though they might have the same layer handle, so we can
// get the updated transform hint from WM.
mSurfaceControl = surface;
- if (mSurfaceControl != nullptr) {
- if (setBackpressureFlag) {
- t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
- layer_state_t::eEnableBackpressure);
- applyTransaction = true;
- }
- mTransformHint = mSurfaceControl->getTransformHint();
- mBufferItemConsumer->setTransformHint(mTransformHint);
+ if (surfaceControlChanged) {
+ t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
+ layer_state_t::eEnableBackpressure);
+ applyTransaction = true;
}
+ mTransformHint = mSurfaceControl->getTransformHint();
+ mBufferItemConsumer->setTransformHint(mTransformHint);
BQA_LOGV("update width=%d height=%d format=%d mTransformHint=%d", width, height, format,
mTransformHint);
@@ -216,11 +218,9 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width,
mSize = mRequestedSize;
SurfaceComposerClient::Transaction* destFrameTransaction =
(outTransaction) ? outTransaction : &t;
- if (mSurfaceControl != nullptr) {
- destFrameTransaction->setDestinationFrame(mSurfaceControl,
- Rect(0, 0, newSize.getWidth(),
- newSize.getHeight()));
- }
+ destFrameTransaction->setDestinationFrame(mSurfaceControl,
+ Rect(0, 0, newSize.getWidth(),
+ newSize.getHeight()));
applyTransaction = true;
}
}
@@ -273,10 +273,10 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/,
// case, we don't actually want to flush the frames in between since they will get
// processed and merged with the sync transaction and released earlier than if they
// were sent to SF
- if (mWaitForTransactionCallback && mNextTransaction == nullptr &&
+ if (mWaitForTransactionCallback && mSyncTransaction == nullptr &&
currFrameNumber >= mLastAcquiredFrameNumber) {
mWaitForTransactionCallback = false;
- flushShadowQueueLocked();
+ flushShadowQueue();
}
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCommittedCallback");
@@ -302,9 +302,6 @@ static void transactionCallbackThunk(void* context, nsecs_t latchTime,
void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
const std::vector<SurfaceControlStats>& stats) {
- std::function<void(int64_t)> transactionCompleteCallback = nullptr;
- uint64_t currFrameNumber = 0;
-
{
std::unique_lock _lock{mMutex};
ATRACE_CALL();
@@ -331,28 +328,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
stat.latchTime,
stat.frameEventStats.dequeueReadyTime);
}
- currFrameNumber = stat.frameEventStats.frameNumber;
-
- if (mTransactionCompleteCallback &&
- currFrameNumber >= mTransactionCompleteFrameNumber) {
- if (currFrameNumber > mTransactionCompleteFrameNumber) {
- BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
- " than expected=%" PRIu64,
- currFrameNumber, mTransactionCompleteFrameNumber);
- }
- transactionCompleteCallback = std::move(mTransactionCompleteCallback);
- mTransactionCompleteFrameNumber = 0;
- }
- std::vector<ReleaseCallbackId> staleReleases;
- for (const auto& [key, value]: mSubmitted) {
- if (currFrameNumber > key.framenumber) {
- staleReleases.push_back(key);
- }
- }
- for (const auto& staleRelease : staleReleases) {
- releaseBufferCallbackLocked(staleRelease, stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE,
- stat.transformHint, stat.currentMaxAcquiredBufferCount);
- }
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
@@ -361,13 +336,8 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
"empty.");
}
-
decStrong((void*)transactionCallbackThunk);
}
-
- if (transactionCompleteCallback) {
- transactionCompleteCallback(currFrameNumber);
- }
}
// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
@@ -375,19 +345,18 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
// Otherwise, this is a no-op.
static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id,
- const sp<Fence>& releaseFence, uint32_t transformHint,
- uint32_t currentMaxAcquiredBufferCount) {
+ const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount) {
sp<BLASTBufferQueue> blastBufferQueue = context.promote();
if (blastBufferQueue) {
- blastBufferQueue->releaseBufferCallback(id, releaseFence, transformHint,
- currentMaxAcquiredBufferCount);
+ blastBufferQueue->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
} else {
ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
}
}
-void BLASTBufferQueue::flushShadowQueueLocked() {
- BQA_LOGV("flushShadowQueueLocked");
+void BLASTBufferQueue::flushShadowQueue() {
+ BQA_LOGV("flushShadowQueue");
int numFramesToFlush = mNumFrameAvailable;
while (numFramesToFlush > 0) {
acquireNextBufferLocked(std::nullopt);
@@ -395,31 +364,13 @@ void BLASTBufferQueue::flushShadowQueueLocked() {
}
}
-void BLASTBufferQueue::flushShadowQueue() {
- std::unique_lock _lock{mMutex};
- flushShadowQueueLocked();
-}
-
-void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id,
- const sp<Fence>& releaseFence, uint32_t transformHint,
- uint32_t currentMaxAcquiredBufferCount) {
- std::unique_lock _lock{mMutex};
- releaseBufferCallbackLocked(id, releaseFence, transformHint, currentMaxAcquiredBufferCount);
-}
-
-void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id,
- const sp<Fence>& releaseFence, uint32_t transformHint,
- uint32_t currentMaxAcquiredBufferCount) {
+void BLASTBufferQueue::releaseBufferCallback(
+ const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount) {
ATRACE_CALL();
+ std::unique_lock _lock{mMutex};
BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
- if (mSurfaceControl != nullptr) {
- mTransformHint = transformHint;
- mSurfaceControl->setTransformHint(transformHint);
- mBufferItemConsumer->setTransformHint(mTransformHint);
- BQA_LOGV("updated mTransformHint=%d", mTransformHint);
- }
-
// Calculate how many buffers we need to hold before we release them back
// to the buffer queue. This will prevent higher latency when we are running
// on a lower refresh rate than the max supported. We only do that for EGL
@@ -429,29 +380,21 @@ void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id,
return it != mSubmitted.end() && it->second.mApi == NATIVE_WINDOW_API_EGL;
}();
- const auto numPendingBuffersToHold =
- isEGL ? std::max(0u, mMaxAcquiredBuffers - currentMaxAcquiredBufferCount) : 0;
- auto rb = ReleasedBuffer{id, releaseFence};
- if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) {
- mPendingRelease.emplace_back(rb);
+ if (currentMaxAcquiredBufferCount) {
+ mCurrentMaxAcquiredBufferCount = *currentMaxAcquiredBufferCount;
}
+ const auto numPendingBuffersToHold =
+ isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
+ mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
+
// Release all buffers that are beyond the ones that we need to hold
while (mPendingRelease.size() > numPendingBuffersToHold) {
- const auto releaseBuffer = mPendingRelease.front();
+ const auto releasedBuffer = mPendingRelease.front();
mPendingRelease.pop_front();
- auto it = mSubmitted.find(releaseBuffer.callbackId);
- if (it == mSubmitted.end()) {
- BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
- releaseBuffer.callbackId.to_string().c_str());
- return;
- }
- mNumAcquired--;
- BQA_LOGV("released %s", id.to_string().c_str());
- mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
- mSubmitted.erase(it);
+ releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
// Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
- // onFrameAvailable handle processing them since it will merge with the nextTransaction.
+ // onFrameAvailable handle processing them since it will merge with the syncTransaction.
if (!mWaitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
}
@@ -463,6 +406,20 @@ void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id,
mCallbackCV.notify_all();
}
+void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
+ const sp<Fence>& releaseFence) {
+ auto it = mSubmitted.find(callbackId);
+ if (it == mSubmitted.end()) {
+ BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
+ callbackId.to_string().c_str());
+ return;
+ }
+ mNumAcquired--;
+ BQA_LOGV("released %s", callbackId.to_string().c_str());
+ mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
+ mSubmitted.erase(it);
+}
+
void BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
ATRACE_CALL();
@@ -543,14 +500,12 @@ void BLASTBufferQueue::acquireNextBufferLocked(
auto releaseBufferCallback =
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
- std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
- std::placeholders::_4);
- t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback);
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+ sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+ t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseBufferCallback);
t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
- t->setAcquireFence(mSurfaceControl,
- bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
@@ -564,7 +519,6 @@ void BLASTBufferQueue::acquireNextBufferLocked(
if (!bufferItem.mIsAutoTimestamp) {
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
- t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
if (!mNextFrameTimelineInfoQueue.empty()) {
t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
@@ -586,21 +540,7 @@ void BLASTBufferQueue::acquireNextBufferLocked(
}
}
- auto mergeTransaction =
- [&t, currentFrameNumber = bufferItem.mFrameNumber](
- std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
- auto& [targetFrameNumber, transaction] = pendingTransaction;
- if (currentFrameNumber < targetFrameNumber) {
- return false;
- }
- t->merge(std::move(transaction));
- return true;
- };
-
- mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
- mPendingTransactions.end(), mergeTransaction),
- mPendingTransactions.end());
-
+ mergePendingTransactions(t, bufferItem.mFrameNumber);
if (applyTransaction) {
t->setApplyToken(mApplyToken).apply();
}
@@ -634,58 +574,83 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() {
mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
}
+void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
+ if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
+ // We are waiting on a previous sync's transaction callback so allow another sync
+ // transaction to proceed.
+ //
+ // We need to first flush out the transactions that were in between the two syncs.
+ // We do this by merging them into mSyncTransaction so any buffer merging will get
+ // a release callback invoked. The release callback will be async so we need to wait
+ // on max acquired to make sure we have the capacity to acquire another buffer.
+ if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGD("waiting to flush shadow queue...");
+ mCallbackCV.wait(lock);
+ }
+ while (mNumFrameAvailable > 0) {
+ // flush out the shadow queue
+ acquireAndReleaseBuffer();
+ }
+ }
+
+ while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGD("waiting for free buffer.");
+ mCallbackCV.wait(lock);
+ }
+}
+
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
- const bool nextTransactionSet = mNextTransaction != nullptr;
- BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet));
- if (nextTransactionSet) {
- if (mWaitForTransactionCallback) {
- // We are waiting on a previous sync's transaction callback so allow another sync
- // transaction to proceed.
- //
- // We need to first flush out the transactions that were in between the two syncs.
- // We do this by merging them into mNextTransaction so any buffer merging will get
- // a release callback invoked. The release callback will be async so we need to wait
- // on max acquired to make sure we have the capacity to acquire another buffer.
- if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting to flush shadow queue...");
- mCallbackCV.wait(_lock);
- }
- while (mNumFrameAvailable > 0) {
- // flush out the shadow queue
- acquireAndReleaseBuffer();
+ const bool syncTransactionSet = mSyncTransaction != nullptr;
+ BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
+
+ if (syncTransactionSet) {
+ bool mayNeedToWaitForBuffer = true;
+ // If we are going to re-use the same mSyncTransaction, release the buffer that may already
+ // be set in the Transaction. This is to allow us a free slot early to continue processing
+ // a new buffer.
+ if (!mAcquireSingleBuffer) {
+ auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
+ if (bufferData) {
+ BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
+ bufferData->frameNumber);
+ releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence);
+ // Because we just released a buffer, we know there's no need to wait for a free
+ // buffer.
+ mayNeedToWaitForBuffer = false;
}
}
- while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting for free buffer.");
- mCallbackCV.wait(_lock);
+ if (mayNeedToWaitForBuffer) {
+ flushAndWaitForFreeBuffer(_lock);
}
}
// add to shadow queue
mNumFrameAvailable++;
- if (mWaitForTransactionCallback && mNumFrameAvailable == 2) {
+ if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
acquireAndReleaseBuffer();
}
ATRACE_INT(mQueuedBufferTrace.c_str(),
mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
- BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
- boolToString(nextTransactionSet));
+ BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber,
+ boolToString(syncTransactionSet));
- if (nextTransactionSet) {
- acquireNextBufferLocked(std::move(mNextTransaction));
+ if (syncTransactionSet) {
+ acquireNextBufferLocked(mSyncTransaction);
// Only need a commit callback when syncing to ensure the buffer that's synced has been sent
// to SF
incStrong((void*)transactionCommittedCallbackThunk);
- mNextTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
+ mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
static_cast<void*>(this));
- mNextTransaction = nullptr;
+ if (mAcquireSingleBuffer) {
+ mSyncTransaction = nullptr;
+ }
mWaitForTransactionCallback = true;
} else if (!mWaitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
@@ -707,9 +672,11 @@ void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
mDequeueTimestamps.erase(bufferId);
};
-void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
+void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t,
+ bool acquireSingleBuffer) {
std::lock_guard _lock{mMutex};
- mNextTransaction = t;
+ mSyncTransaction = t;
+ mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true;
}
bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
@@ -734,24 +701,13 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
return mSize != bufferSize;
}
-void BLASTBufferQueue::setTransactionCompleteCallback(
- uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
- std::lock_guard _lock{mMutex};
- if (transactionCompleteCallback == nullptr) {
- mTransactionCompleteCallback = nullptr;
- } else {
- mTransactionCompleteCallback = std::move(transactionCompleteCallback);
- mTransactionCompleteFrameNumber = frameNumber;
- }
-}
-
// Check if we have acquired the maximum number of buffers.
// Consumer can acquire an additional buffer if that buffer is not droppable. Set
// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1);
- return mNumAcquired == maxAcquiredBuffers;
+ return mNumAcquired >= maxAcquiredBuffers;
}
class BBQSurface : public Surface {
@@ -852,6 +808,32 @@ void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transacti
}
}
+void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) {
+ std::lock_guard _lock{mMutex};
+
+ SurfaceComposerClient::Transaction t;
+ mergePendingTransactions(&t, frameNumber);
+ t.setApplyToken(mApplyToken).apply();
+}
+
+void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
+ uint64_t frameNumber) {
+ auto mergeTransaction =
+ [&t, currentFrameNumber = frameNumber](
+ std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+ auto& [targetFrameNumber, transaction] = pendingTransaction;
+ if (currentFrameNumber < targetFrameNumber) {
+ return false;
+ }
+ t->merge(std::move(transaction));
+ return true;
+ };
+
+ mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
+ mPendingTransactions.end(), mergeTransaction),
+ mPendingTransactions.end());
+}
+
// Maintains a single worker thread per process that services a list of runnables.
class AsyncWorker : public Singleton<AsyncWorker> {
private:
@@ -999,4 +981,53 @@ uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() {
return mLastAcquiredFrameNumber;
}
+void BLASTBufferQueue::abandon() {
+ std::unique_lock _lock{mMutex};
+ // flush out the shadow queue
+ while (mNumFrameAvailable > 0) {
+ acquireAndReleaseBuffer();
+ }
+
+ // Clear submitted buffer states
+ mNumAcquired = 0;
+ mSubmitted.clear();
+ mPendingRelease.clear();
+
+ if (!mPendingTransactions.empty()) {
+ BQA_LOGD("Applying pending transactions on abandon %d",
+ static_cast<uint32_t>(mPendingTransactions.size()));
+ SurfaceComposerClient::Transaction t;
+ mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
+ t.setApplyToken(mApplyToken).apply();
+ }
+
+ // Clear sync states
+ if (mWaitForTransactionCallback) {
+ BQA_LOGD("mWaitForTransactionCallback cleared");
+ mWaitForTransactionCallback = false;
+ }
+
+ if (mSyncTransaction != nullptr) {
+ BQA_LOGD("mSyncTransaction cleared mAcquireSingleBuffer=%s",
+ mAcquireSingleBuffer ? "true" : "false");
+ mSyncTransaction = nullptr;
+ mAcquireSingleBuffer = false;
+ }
+
+ // abandon buffer queue
+ if (mBufferItemConsumer != nullptr) {
+ mBufferItemConsumer->abandon();
+ mBufferItemConsumer->setFrameAvailableListener(nullptr);
+ mBufferItemConsumer->setBufferFreedListener(nullptr);
+ }
+ mBufferItemConsumer = nullptr;
+ mConsumer = nullptr;
+ mProducer = nullptr;
+}
+
+bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const {
+ std::unique_lock _lock{mMutex};
+ return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
+}
+
} // namespace android
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 46800f2f14..ee8008270e 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -146,6 +146,19 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) {
return 1; // keep the callback
}
+void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event,
+ VsyncEventData* outVsyncEventData) const {
+ for (size_t i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+ DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline =
+ event.vsync.frameTimelines[i];
+ outVsyncEventData->frameTimelines[i] = {.id = receiverTimeline.vsyncId,
+ .deadlineTimestamp =
+ receiverTimeline.deadlineTimestamp,
+ .expectedPresentTime =
+ receiverTimeline.expectedVSyncTimestamp};
+ }
+}
+
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
PhysicalDisplayId* outDisplayId,
uint32_t* outCount,
@@ -169,6 +182,9 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
outVsyncEventData->id = ev.vsync.vsyncId;
outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
outVsyncEventData->frameInterval = ev.vsync.frameInterval;
+ outVsyncEventData->preferredFrameTimelineIndex =
+ ev.vsync.preferredFrameTimelineIndex;
+ populateFrameTimelines(ev, outVsyncEventData);
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 03b33c7330..b916e48f79 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -19,7 +19,6 @@
#include <utils/Errors.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp
new file mode 100644
index 0000000000..52d9540eeb
--- /dev/null
+++ b/libs/gui/DisplayInfo.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DisplayInfo"
+
+#include <binder/Parcel.h>
+#include <gui/DisplayInfo.h>
+#include <private/gui/ParcelUtils.h>
+
+#include <log/log.h>
+
+namespace android::gui {
+
+// --- DisplayInfo ---
+
+status_t DisplayInfo::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ SAFE_PARCEL(parcel->readInt32, &displayId);
+ SAFE_PARCEL(parcel->readInt32, &logicalWidth);
+ SAFE_PARCEL(parcel->readInt32, &logicalHeight);
+ SAFE_PARCEL(parcel->readFloat, &dsdx);
+ SAFE_PARCEL(parcel->readFloat, &dtdx);
+ SAFE_PARCEL(parcel->readFloat, &tx);
+ SAFE_PARCEL(parcel->readFloat, &dtdy);
+ SAFE_PARCEL(parcel->readFloat, &dsdy);
+ SAFE_PARCEL(parcel->readFloat, &ty);
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+
+ return OK;
+}
+
+status_t DisplayInfo::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeInt32, displayId);
+ SAFE_PARCEL(parcel->writeInt32, logicalWidth);
+ SAFE_PARCEL(parcel->writeInt32, logicalHeight);
+ SAFE_PARCEL(parcel->writeFloat, transform.dsdx());
+ SAFE_PARCEL(parcel->writeFloat, transform.dtdx());
+ SAFE_PARCEL(parcel->writeFloat, transform.tx());
+ SAFE_PARCEL(parcel->writeFloat, transform.dtdy());
+ SAFE_PARCEL(parcel->writeFloat, transform.dsdy());
+ SAFE_PARCEL(parcel->writeFloat, transform.ty());
+
+ return OK;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/FrameTimelineInfo.cpp b/libs/gui/FrameTimelineInfo.cpp
index 9231a570fc..3800b88ab0 100644
--- a/libs/gui/FrameTimelineInfo.cpp
+++ b/libs/gui/FrameTimelineInfo.cpp
@@ -33,12 +33,14 @@ namespace android {
status_t FrameTimelineInfo::write(Parcel& output) const {
SAFE_PARCEL(output.writeInt64, vsyncId);
SAFE_PARCEL(output.writeInt32, inputEventId);
+ SAFE_PARCEL(output.writeInt64, startTimeNanos);
return NO_ERROR;
}
status_t FrameTimelineInfo::read(const Parcel& input) {
SAFE_PARCEL(input.readInt64, &vsyncId);
SAFE_PARCEL(input.readInt32, &inputEventId);
+ SAFE_PARCEL(input.readInt64, &startTimeNanos);
return NO_ERROR;
}
@@ -48,16 +50,19 @@ void FrameTimelineInfo::merge(const FrameTimelineInfo& other) {
if (other.vsyncId > vsyncId) {
vsyncId = other.vsyncId;
inputEventId = other.inputEventId;
+ startTimeNanos = other.startTimeNanos;
}
} else if (vsyncId == INVALID_VSYNC_ID) {
vsyncId = other.vsyncId;
inputEventId = other.inputEventId;
+ startTimeNanos = other.startTimeNanos;
}
}
void FrameTimelineInfo::clear() {
vsyncId = INVALID_VSYNC_ID;
inputEventId = IInputConstants::INVALID_INPUT_EVENT_ID;
+ startTimeNanos = 0;
}
}; // namespace android
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
deleted file mode 100644
index c0e246fa15..0000000000
--- a/libs/gui/IDisplayEventConnection.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gui/IDisplayEventConnection.h>
-
-#include <private/gui/BitTube.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
- SET_VSYNC_RATE,
- REQUEST_NEXT_VSYNC,
- LAST = REQUEST_NEXT_VSYNC,
-};
-
-} // Anonymous namespace
-
-class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> {
-public:
- explicit BpDisplayEventConnection(const sp<IBinder>& impl)
- : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {}
-
- ~BpDisplayEventConnection() override;
-
- status_t stealReceiveChannel(gui::BitTube* outChannel) override {
- return callRemote<decltype(
- &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL,
- outChannel);
- }
-
- status_t setVsyncRate(uint32_t count) override {
- return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE,
- count);
- }
-
- void requestNextVsync() override {
- callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
- Tag::REQUEST_NEXT_VSYNC);
- }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpDisplayEventConnection::~BpDisplayEventConnection() = default;
-
-IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
-
-status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::STEAL_RECEIVE_CHANNEL:
- return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel);
- case Tag::SET_VSYNC_RATE:
- return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
- case Tag::REQUEST_NEXT_VSYNC:
- return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
- }
-}
-
-} // namespace android
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp
deleted file mode 100644
index 40cbfceaf7..0000000000
--- a/libs/gui/IRegionSamplingListener.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IRegionSamplingListener"
-//#define LOG_NDEBUG 0
-
-#include <gui/IRegionSamplingListener.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION,
- LAST = ON_SAMPLE_COLLECTED,
-};
-
-} // Anonymous namespace
-
-class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> {
-public:
- explicit BpRegionSamplingListener(const sp<IBinder>& impl)
- : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {}
-
- ~BpRegionSamplingListener() override;
-
- void onSampleCollected(float medianLuma) override {
- callRemoteAsync<decltype(
- &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma);
- }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpRegionSamplingListener::~BpRegionSamplingListener() = default;
-
-IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener");
-
-status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::ON_SAMPLE_COLLECTED:
- return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected);
- }
-}
-
-} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 1726761785..b7594df2fb 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,13 +17,13 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
+#include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/ITransactionTraceListener.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
@@ -44,6 +44,8 @@
namespace android {
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using gui::IWindowInfosListener;
using ui::ColorMode;
@@ -124,11 +126,11 @@ public:
return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
}
- status_t captureDisplay(uint64_t displayOrLayerStack,
+ status_t captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+ SAFE_PARCEL(data.writeUint64, displayId.value);
SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
@@ -282,9 +284,14 @@ public:
NO_ERROR) {
std::vector<uint64_t> rawIds;
if (reply.readUint64Vector(&rawIds) == NO_ERROR) {
- std::vector<PhysicalDisplayId> displayIds(rawIds.size());
- std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(),
- [](uint64_t rawId) { return PhysicalDisplayId(rawId); });
+ std::vector<PhysicalDisplayId> displayIds;
+ displayIds.reserve(rawIds.size());
+
+ for (const uint64_t rawId : rawIds) {
+ if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+ displayIds.push_back(*id);
+ }
+ }
return displayIds;
}
}
@@ -299,8 +306,11 @@ public:
&reply);
uint64_t rawId;
SAFE_PARCEL(reply.readUint64, &rawId);
- *displayId = PhysicalDisplayId(rawId);
- return NO_ERROR;
+ if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+ *displayId = *id;
+ return NO_ERROR;
+ }
+ return NAME_NOT_FOUND;
}
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override {
@@ -418,6 +428,94 @@ public:
return static_cast<status_t>(reply.readInt32());
}
+ // TODO(b/213909104) : Add unit tests to verify surface flinger boot time APIs
+ status_t getBootDisplayModeSupport(bool* outSupport) const override {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("getBootDisplayModeSupport: failed to write interface token: %d", error);
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::GET_BOOT_DISPLAY_MODE_SUPPORT, data, &reply);
+ if (error != NO_ERROR) {
+ ALOGE("getBootDisplayModeSupport: failed to transact: %d", error);
+ return error;
+ }
+ bool support;
+ error = reply.readBool(&support);
+ if (error != NO_ERROR) {
+ ALOGE("getBootDisplayModeSupport: failed to read support: %d", error);
+ return error;
+ }
+ *outSupport = support;
+ return NO_ERROR;
+ }
+
+ status_t setBootDisplayMode(const sp<IBinder>& display,
+ ui::DisplayModeId displayModeId) override {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setBootDisplayMode failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setBootDisplayMode failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = data.writeInt32(displayModeId);
+ if (result != NO_ERROR) {
+ ALOGE("setBootDisplayMode failed to writeIint32: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_BOOT_DISPLAY_MODE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setBootDisplayMode failed to transact: %d", result);
+ }
+ return result;
+ }
+
+ status_t clearBootDisplayMode(const sp<IBinder>& display) override {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("clearBootDisplayMode failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("clearBootDisplayMode failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::CLEAR_BOOT_DISPLAY_MODE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("clearBootDisplayMode failed to transact: %d", result);
+ }
+ return result;
+ }
+
+ status_t getPreferredBootDisplayMode(const sp<IBinder>& display,
+ ui::DisplayModeId* displayModeId) override {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getPreferredBootDisplayMode failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getPreferredBootDisplayMode failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_PREFERRED_BOOT_DISPLAY_MODE, data,
+ &reply);
+ if (result == NO_ERROR) {
+ reply.writeInt32(*displayModeId);
+ }
+ return result;
+ }
+
void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1124,6 +1222,34 @@ public:
return NO_ERROR;
}
+ status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const override {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayDecorationSupport: failed to write interface token: %d", error);
+ return error;
+ }
+ error = data.writeStrongBinder(displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayDecorationSupport: failed to write display token: %d", error);
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::GET_DISPLAY_DECORATION_SUPPORT, data, &reply);
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayDecorationSupport: failed to transact: %d", error);
+ return error;
+ }
+ bool support;
+ error = reply.readBool(&support);
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayDecorationSupport: failed to read support: %d", error);
+ return error;
+ }
+ *outSupport = support;
+ return NO_ERROR;
+ }
+
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy) override {
Parcel data, reply;
@@ -1142,41 +1268,6 @@ public:
return reply.readInt32();
}
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override {
- if (!outToken) return BAD_VALUE;
-
- Parcel data, reply;
- status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed writing interface token: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = remote()->transact(BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, data,
- &reply);
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed to transact: %s (%d)", strerror(-err),
- err);
- return err;
- }
-
- err = reply.readInt32();
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: call failed: %s (%d)", strerror(-err), err);
- return err;
- }
-
- err = reply.readStrongBinder(outToken);
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed reading binder token: %s (%d)",
- strerror(-err), err);
- return err;
- }
-
- return NO_ERROR;
- }
-
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) override {
Parcel data, reply;
@@ -1255,6 +1346,21 @@ public:
SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener));
return remote()->transact(BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, data, &reply);
}
+
+ status_t setOverrideFrameRate(uid_t uid, float frameRate) override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeUint32, uid);
+ SAFE_PARCEL(data.writeFloat, frameRate);
+
+ status_t err = remote()->transact(BnSurfaceComposer::SET_OVERRIDE_FRAME_RATE, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("setOverrideFrameRate: failed to transact %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ return NO_ERROR;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -1355,12 +1461,15 @@ status_t BnSurfaceComposer::onTransact(
}
case CAPTURE_DISPLAY_BY_ID: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- uint64_t displayOrLayerStack = 0;
+ uint64_t value;
+ SAFE_PARCEL(data.readUint64, &value);
+ const auto id = DisplayId::fromValue(value);
+ if (!id) return BAD_VALUE;
+
sp<IScreenCaptureListener> captureListener;
- SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
SAFE_PARCEL(data.readStrongBinder, &captureListener);
- return captureDisplay(displayOrLayerStack, captureListener);
+ return captureDisplay(*id, captureListener);
}
case CAPTURE_LAYERS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1427,9 +1536,9 @@ status_t BnSurfaceComposer::onTransact(
}
case GET_PHYSICAL_DISPLAY_TOKEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- PhysicalDisplayId displayId(data.readUint64());
- sp<IBinder> display = getPhysicalDisplayToken(displayId);
- reply->writeStrongBinder(display);
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(data.readUint64());
+ if (!id) return BAD_VALUE;
+ reply->writeStrongBinder(getPhysicalDisplayToken(*id));
return NO_ERROR;
}
case GET_DISPLAY_STATE: {
@@ -1515,6 +1624,56 @@ status_t BnSurfaceComposer::onTransact(
result = reply->writeInt32(result);
return result;
}
+ case GET_BOOT_DISPLAY_MODE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ bool support = false;
+ status_t result = getBootDisplayModeSupport(&support);
+ if (result == NO_ERROR) {
+ reply->writeBool(support);
+ }
+ return result;
+ }
+ case SET_BOOT_DISPLAY_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setBootDisplayMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ ui::DisplayModeId displayModeId;
+ result = data.readInt32(&displayModeId);
+ if (result != NO_ERROR) {
+ ALOGE("setBootDisplayMode failed to readInt32: %d", result);
+ return result;
+ }
+ return setBootDisplayMode(display, displayModeId);
+ }
+ case CLEAR_BOOT_DISPLAY_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("clearBootDisplayMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ return clearBootDisplayMode(display);
+ }
+ case GET_PREFERRED_BOOT_DISPLAY_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getPreferredBootDisplayMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ ui::DisplayModeId displayModeId;
+ result = getPreferredBootDisplayMode(display, &displayModeId);
+ if (result == NO_ERROR) {
+ reply->writeInt32(displayModeId);
+ }
+ return result;
+ }
case SET_AUTO_LOW_LATENCY_MODE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = nullptr;
@@ -2038,6 +2197,19 @@ status_t BnSurfaceComposer::onTransact(
return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
lightRadius);
}
+ case GET_DISPLAY_DECORATION_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken;
+ status_t error = data.readNullableStrongBinder(&displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayDecorationSupport: failed to read display token: %d", error);
+ return error;
+ }
+ bool support = false;
+ error = getDisplayDecorationSupport(displayToken, &support);
+ reply->writeBool(support);
+ return error;
+ }
case SET_FRAME_RATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> binder;
@@ -2062,16 +2234,6 @@ status_t BnSurfaceComposer::onTransact(
reply->writeInt32(result);
return NO_ERROR;
}
- case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> token;
- status_t result = acquireFrameRateFlexibilityToken(&token);
- reply->writeInt32(result);
- if (result == NO_ERROR) {
- reply->writeStrongBinder(token);
- }
- return NO_ERROR;
- }
case SET_FRAME_TIMELINE_INFO: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> binder;
@@ -2159,6 +2321,17 @@ status_t BnSurfaceComposer::onTransact(
return removeWindowInfosListener(listener);
}
+ case SET_OVERRIDE_FRAME_RATE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ uid_t uid;
+ SAFE_PARCEL(data.readUint32, &uid);
+
+ float frameRate;
+ SAFE_PARCEL(data.readFloat, &frameRate);
+
+ return setOverrideFrameRate(uid, frameRate);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 98e8b548bd..aa7ebc9eb3 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -254,11 +254,10 @@ public:
}
void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override {
+ uint32_t currentMaxAcquiredBufferCount) override {
callRemoteAsync<decltype(
&ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
callbackId, releaseFence,
- transformHint,
currentMaxAcquiredBufferCount);
}
};
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 77a883b332..27d86bb4e2 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -41,7 +41,6 @@ layer_state_t::layer_state_t()
z(0),
w(0),
h(0),
- layerStack(0),
alpha(0),
flags(0),
mask(0),
@@ -51,7 +50,6 @@ layer_state_t::layer_state_t()
transform(0),
transformToDisplayInverse(false),
crop(Rect::INVALID_RECT),
- orientedDisplaySpaceRect(Rect::INVALID_RECT),
dataspace(ui::Dataspace::UNKNOWN),
surfaceDamageRegion(),
api(-1),
@@ -65,12 +63,10 @@ layer_state_t::layer_state_t()
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
fixedTransformHint(ui::Transform::ROT_INVALID),
- frameNumber(0),
autoRefresh(false),
isTrustedOverlay(false),
bufferCrop(Rect::INVALID_RECT),
destinationFrame(Rect::INVALID_RECT),
- releaseBufferListener(nullptr),
dropInputMode(gui::DropInputMode::NONE) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
@@ -87,7 +83,7 @@ status_t layer_state_t::write(Parcel& output) const
SAFE_PARCEL(output.writeInt32, z);
SAFE_PARCEL(output.writeUint32, w);
SAFE_PARCEL(output.writeUint32, h);
- SAFE_PARCEL(output.writeUint32, layerStack);
+ SAFE_PARCEL(output.writeUint32, layerStack.id);
SAFE_PARCEL(output.writeFloat, alpha);
SAFE_PARCEL(output.writeUint32, flags);
SAFE_PARCEL(output.writeUint32, mask);
@@ -103,21 +99,6 @@ status_t layer_state_t::write(Parcel& output) const
SAFE_PARCEL(output.write, transparentRegion);
SAFE_PARCEL(output.writeUint32, transform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
- SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
-
- if (buffer) {
- SAFE_PARCEL(output.writeBool, true);
- SAFE_PARCEL(output.write, *buffer);
- } else {
- SAFE_PARCEL(output.writeBool, false);
- }
-
- if (acquireFence) {
- SAFE_PARCEL(output.writeBool, true);
- SAFE_PARCEL(output.write, *acquireFence);
- } else {
- SAFE_PARCEL(output.writeBool, false);
- }
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
SAFE_PARCEL(output.write, hdrMetadata);
@@ -134,8 +115,6 @@ status_t layer_state_t::write(Parcel& output) const
SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
SAFE_PARCEL(output.writeFloat, cornerRadius);
SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
- SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
- SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
SAFE_PARCEL(output.writeParcelable, metadata);
SAFE_PARCEL(output.writeFloat, bgColorAlpha);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace));
@@ -152,9 +131,7 @@ status_t layer_state_t::write(Parcel& output) const
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
- SAFE_PARCEL(output.writeUint64, frameNumber);
SAFE_PARCEL(output.writeBool, autoRefresh);
- SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
SAFE_PARCEL(output.writeUint32, blurRegions.size());
for (auto region : blurRegions) {
@@ -175,8 +152,9 @@ status_t layer_state_t::write(Parcel& output) const
SAFE_PARCEL(output.write, destinationFrame);
SAFE_PARCEL(output.writeBool, isTrustedOverlay);
- SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode));
+ SAFE_PARCEL(output.writeNullableParcelable,
+ bufferData ? std::make_optional<BufferData>(*bufferData) : std::nullopt);
return NO_ERROR;
}
@@ -190,7 +168,7 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.readInt32, &z);
SAFE_PARCEL(input.readUint32, &w);
SAFE_PARCEL(input.readUint32, &h);
- SAFE_PARCEL(input.readUint32, &layerStack);
+ SAFE_PARCEL(input.readUint32, &layerStack.id);
SAFE_PARCEL(input.readFloat, &alpha);
SAFE_PARCEL(input.readUint32, &flags);
@@ -216,20 +194,6 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.read, transparentRegion);
SAFE_PARCEL(input.readUint32, &transform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
- SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
-
- bool tmpBool = false;
- SAFE_PARCEL(input.readBool, &tmpBool);
- if (tmpBool) {
- buffer = new GraphicBuffer();
- SAFE_PARCEL(input.read, *buffer);
- }
-
- SAFE_PARCEL(input.readBool, &tmpBool);
- if (tmpBool) {
- acquireFence = new Fence();
- SAFE_PARCEL(input.read, *acquireFence);
- }
uint32_t tmpUint32 = 0;
SAFE_PARCEL(input.readUint32, &tmpUint32);
@@ -238,6 +202,8 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.read, hdrMetadata);
SAFE_PARCEL(input.read, surfaceDamageRegion);
SAFE_PARCEL(input.readInt32, &api);
+
+ bool tmpBool = false;
SAFE_PARCEL(input.readBool, &tmpBool);
if (tmpBool) {
sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
@@ -246,10 +212,6 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
SAFE_PARCEL(input.readFloat, &cornerRadius);
SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
- sp<IBinder> tmpBinder;
- SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
- cachedBuffer.token = tmpBinder;
- SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
SAFE_PARCEL(input.readParcelable, &metadata);
SAFE_PARCEL(input.readFloat, &bgColorAlpha);
@@ -274,15 +236,8 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
- SAFE_PARCEL(input.readUint64, &frameNumber);
SAFE_PARCEL(input.readBool, &autoRefresh);
- tmpBinder = nullptr;
- SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
- if (tmpBinder) {
- releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
- }
-
uint32_t numRegions = 0;
SAFE_PARCEL(input.readUint32, &numRegions);
blurRegions.clear();
@@ -306,11 +261,12 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.read, destinationFrame);
SAFE_PARCEL(input.readBool, &isTrustedOverlay);
- SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint);
-
uint32_t mode;
SAFE_PARCEL(input.readUint32, &mode);
dropInputMode = static_cast<gui::DropInputMode>(mode);
+ std::optional<BufferData> tmpBufferData;
+ SAFE_PARCEL(input.readParcelable, &tmpBufferData);
+ bufferData = tmpBufferData ? std::make_shared<BufferData>(*tmpBufferData) : nullptr;
return NO_ERROR;
}
@@ -322,21 +278,14 @@ status_t ComposerState::read(const Parcel& input) {
return state.read(input);
}
-DisplayState::DisplayState()
- : what(0),
- layerStack(0),
- flags(0),
- layerStackSpaceRect(Rect::EMPTY_RECT),
- orientedDisplaySpaceRect(Rect::EMPTY_RECT),
- width(0),
- height(0) {}
+DisplayState::DisplayState() = default;
status_t DisplayState::write(Parcel& output) const {
SAFE_PARCEL(output.writeStrongBinder, token);
SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));
SAFE_PARCEL(output.writeUint32, what);
- SAFE_PARCEL(output.writeUint32, layerStack);
SAFE_PARCEL(output.writeUint32, flags);
+ SAFE_PARCEL(output.writeUint32, layerStack.id);
SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));
SAFE_PARCEL(output.write, layerStackSpaceRect);
SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
@@ -352,8 +301,8 @@ status_t DisplayState::read(const Parcel& input) {
surface = interface_cast<IGraphicBufferProducer>(tmpBinder);
SAFE_PARCEL(input.readUint32, &what);
- SAFE_PARCEL(input.readUint32, &layerStack);
SAFE_PARCEL(input.readUint32, &flags);
+ SAFE_PARCEL(input.readUint32, &layerStack.id);
uint32_t tmpUint = 0;
SAFE_PARCEL(input.readUint32, &tmpUint);
orientation = ui::toRotation(tmpUint);
@@ -468,12 +417,7 @@ void layer_state_t::merge(const layer_state_t& other) {
}
if (other.what & eBufferChanged) {
what |= eBufferChanged;
- buffer = other.buffer;
- releaseBufferEndpoint = other.releaseBufferEndpoint;
- }
- if (other.what & eAcquireFenceChanged) {
- what |= eAcquireFenceChanged;
- acquireFence = other.acquireFence;
+ bufferData = other.bufferData;
}
if (other.what & eDataspaceChanged) {
what |= eDataspaceChanged;
@@ -506,11 +450,6 @@ void layer_state_t::merge(const layer_state_t& other) {
what |= eInputInfoChanged;
windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle);
}
- if (other.what & eCachedBufferChanged) {
- what |= eCachedBufferChanged;
- cachedBuffer = other.cachedBuffer;
- releaseBufferEndpoint = other.releaseBufferEndpoint;
- }
if (other.what & eBackgroundColorChanged) {
what |= eBackgroundColorChanged;
color = other.color;
@@ -539,10 +478,6 @@ void layer_state_t::merge(const layer_state_t& other) {
what |= eFixedTransformHintChanged;
fixedTransformHint = other.fixedTransformHint;
}
- if (other.what & eFrameNumberChanged) {
- what |= eFrameNumberChanged;
- frameNumber = other.frameNumber;
- }
if (other.what & eAutoRefreshChanged) {
what |= eAutoRefreshChanged;
autoRefresh = other.autoRefresh;
@@ -551,13 +486,6 @@ void layer_state_t::merge(const layer_state_t& other) {
what |= eTrustedOverlayChanged;
isTrustedOverlay = other.isTrustedOverlay;
}
- if (other.what & eReleaseBufferListenerChanged) {
- if (releaseBufferListener) {
- ALOGW("Overriding releaseBufferListener");
- }
- what |= eReleaseBufferListenerChanged;
- releaseBufferListener = other.releaseBufferListener;
- }
if (other.what & eStretchChanged) {
what |= eStretchChanged;
stretchEffect = other.stretchEffect;
@@ -570,6 +498,9 @@ void layer_state_t::merge(const layer_state_t& other) {
what |= eDestinationFrameChanged;
destinationFrame = other.destinationFrame;
}
+ if (other.what & eProducerDisconnect) {
+ what |= eProducerDisconnect;
+ }
if (other.what & eDropInputModeChanged) {
what |= eDropInputModeChanged;
dropInputMode = other.dropInputMode;
@@ -580,17 +511,17 @@ void layer_state_t::merge(const layer_state_t& other) {
}
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
- "other.what=0x%" PRIu64 " what=0x%" PRIu64,
- other.what, what);
+ "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
+ other.what, what, (other.what & what) ^ other.what);
}
}
bool layer_state_t::hasBufferChanges() const {
- return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged);
+ return what & layer_state_t::eBufferChanged;
}
bool layer_state_t::hasValidBuffer() const {
- return buffer || cachedBuffer.isValid();
+ return bufferData && (bufferData->buffer || bufferData->cachedBuffer.isValid());
}
status_t layer_state_t::matrix22_t::write(Parcel& output) const {
@@ -622,9 +553,7 @@ bool InputWindowCommands::merge(const InputWindowCommands& other) {
}
bool InputWindowCommands::empty() const {
- bool empty = true;
- empty = focusRequests.empty() && !syncInputWindows;
- return empty;
+ return focusRequests.empty() && !syncInputWindows;
}
void InputWindowCommands::clear() {
@@ -751,4 +680,70 @@ status_t LayerCaptureArgs::read(const Parcel& input) {
return NO_ERROR;
}
+ReleaseCallbackId BufferData::generateReleaseCallbackId() const {
+ return {buffer->getId(), frameNumber};
+}
+
+status_t BufferData::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeInt32, flags.get());
+
+ if (buffer) {
+ SAFE_PARCEL(output->writeBool, true);
+ SAFE_PARCEL(output->write, *buffer);
+ } else {
+ SAFE_PARCEL(output->writeBool, false);
+ }
+
+ if (acquireFence) {
+ SAFE_PARCEL(output->writeBool, true);
+ SAFE_PARCEL(output->write, *acquireFence);
+ } else {
+ SAFE_PARCEL(output->writeBool, false);
+ }
+
+ SAFE_PARCEL(output->writeUint64, frameNumber);
+ SAFE_PARCEL(output->writeStrongBinder, IInterface::asBinder(releaseBufferListener));
+ SAFE_PARCEL(output->writeStrongBinder, releaseBufferEndpoint);
+
+ SAFE_PARCEL(output->writeStrongBinder, cachedBuffer.token.promote());
+ SAFE_PARCEL(output->writeUint64, cachedBuffer.id);
+
+ return NO_ERROR;
+}
+
+status_t BufferData::readFromParcel(const Parcel* input) {
+ int32_t tmpInt32;
+ SAFE_PARCEL(input->readInt32, &tmpInt32);
+ flags = Flags<BufferDataChange>(tmpInt32);
+
+ bool tmpBool = false;
+ SAFE_PARCEL(input->readBool, &tmpBool);
+ if (tmpBool) {
+ buffer = new GraphicBuffer();
+ SAFE_PARCEL(input->read, *buffer);
+ }
+
+ SAFE_PARCEL(input->readBool, &tmpBool);
+ if (tmpBool) {
+ acquireFence = new Fence();
+ SAFE_PARCEL(input->read, *acquireFence);
+ }
+
+ SAFE_PARCEL(input->readUint64, &frameNumber);
+
+ sp<IBinder> tmpBinder = nullptr;
+ SAFE_PARCEL(input->readNullableStrongBinder, &tmpBinder);
+ if (tmpBinder) {
+ releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+ }
+ SAFE_PARCEL(input->readNullableStrongBinder, &releaseBufferEndpoint);
+
+ tmpBinder = nullptr;
+ SAFE_PARCEL(input->readNullableStrongBinder, &tmpBinder);
+ cachedBuffer.token = tmpBinder;
+ SAFE_PARCEL(input->readUint64, &cachedBuffer.id);
+
+ return NO_ERROR;
+}
+
}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 353a91d062..20c41460d4 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1846,9 +1846,10 @@ int Surface::dispatchSetFrameTimelineInfo(va_list args) {
ATRACE_CALL();
auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
+ auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
ALOGV("Surface::%s", __func__);
- return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId});
+ return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId, startTimeNanos});
}
bool Surface::transformToDisplayInverse() const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 725ea6571d..31456cda7e 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -53,6 +53,7 @@
namespace android {
using gui::FocusRequest;
+using gui::IRegionSamplingListener;
using gui::WindowInfo;
using gui::WindowInfoHandle;
using gui::WindowInfosListener;
@@ -61,6 +62,14 @@ using ui::ColorMode;
ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
+namespace {
+// Initialize transaction id counter used to generate transaction ids
+std::atomic<uint32_t> idCounter = 0;
+int64_t generateId() {
+ return (((int64_t)getpid()) << 32) | ++idCounter;
+}
+} // namespace
+
ComposerService::ComposerService()
: Singleton<ComposerService>() {
Mutex::Autolock _l(mLock);
@@ -214,12 +223,6 @@ void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbac
mReleaseBufferCallbacks[callbackId] = listener;
}
-void TransactionCompletedListener::removeReleaseBufferCallback(
- const ReleaseCallbackId& callbackId) {
- std::scoped_lock<std::mutex> lock(mMutex);
- mReleaseBufferCallbacks.erase(callbackId);
-}
-
void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
@@ -296,7 +299,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
transactionStats.latchTime, surfaceStats.acquireTime,
transactionStats.presentFence,
surfaceStats.previousReleaseFence, surfaceStats.transformHint,
- surfaceStats.eventStats, surfaceStats.currentMaxAcquiredBufferCount);
+ surfaceStats.eventStats);
}
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -321,7 +324,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
transactionStats.latchTime, surfaceStats.acquireTime,
transactionStats.presentFence,
surfaceStats.previousReleaseFence, surfaceStats.transformHint,
- surfaceStats.eventStats, surfaceStats.currentMaxAcquiredBufferCount);
+ surfaceStats.eventStats);
if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
callbacksMap[callbackId]
.surfaceControls[surfaceStats.surfaceControl]
@@ -343,7 +346,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
surfaceStats.previousReleaseFence
? surfaceStats.previousReleaseFence
: Fence::NO_FENCE,
- surfaceStats.transformHint,
surfaceStats.currentMaxAcquiredBufferCount);
}
}
@@ -359,6 +361,10 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
// through all until the SC is found.
int32_t layerId = -1;
for (auto callbackId : transactionStats.callbackIds) {
+ if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
+ // We only want to run the stats callback for ON_COMPLETE
+ continue;
+ }
sp<SurfaceControl> sc =
callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl];
if (sc != nullptr) {
@@ -367,7 +373,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
}
}
- {
+ if (layerId != -1) {
// Acquire surface stats listener lock such that we guarantee that after calling
// unregister, there won't be any further callback.
std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
@@ -389,7 +395,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
}
void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
- sp<Fence> releaseFence, uint32_t transformHint,
+ sp<Fence> releaseFence,
uint32_t currentMaxAcquiredBufferCount) {
ReleaseBufferCallback callback;
{
@@ -401,7 +407,11 @@ void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
callbackId.to_string().c_str());
return;
}
- callback(callbackId, releaseFence, transformHint, currentMaxAcquiredBufferCount);
+ std::optional<uint32_t> optionalMaxAcquiredBufferCount =
+ currentMaxAcquiredBufferCount == UINT_MAX
+ ? std::nullopt
+ : std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
+ callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
}
ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -416,6 +426,14 @@ ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLock
return callback;
}
+void TransactionCompletedListener::removeReleaseBufferCallback(
+ const ReleaseCallbackId& callbackId) {
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ popReleaseBufferCallbackLocked(callbackId);
+ }
+}
+
// ---------------------------------------------------------------------------
void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -525,10 +543,6 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId) {
// ---------------------------------------------------------------------------
-// Initialize transaction id counter used to generate transaction ids
-// Transactions will start counting at 1, 0 is used for invalid transactions
-std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1;
-
SurfaceComposerClient::Transaction::Transaction() {
mId = generateId();
}
@@ -560,9 +574,6 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) {
return nullptr;
}
-int64_t SurfaceComposerClient::Transaction::generateId() {
- return (((int64_t)getpid()) << 32) | idCounter++;
-}
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint32_t forceSynchronous = parcel->readUint32();
@@ -712,11 +723,34 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
return NO_ERROR;
}
+void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_state_t& state) {
+ if (!(state.what & layer_state_t::eBufferChanged)) {
+ return;
+ }
+
+ auto listener = state.bufferData->releaseBufferListener;
+ sp<Fence> fence =
+ state.bufferData->acquireFence ? state.bufferData->acquireFence : Fence::NO_FENCE;
+ if (state.bufferData->releaseBufferEndpoint ==
+ IInterface::asBinder(TransactionCompletedListener::getIInstance())) {
+ // if the callback is in process, run on a different thread to avoid any lock contigency
+ // issues in the client.
+ SurfaceComposerClient::getDefault()
+ ->mReleaseCallbackThread
+ .addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence);
+ } else {
+ listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX);
+ }
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
for (auto const& [handle, composerState] : other.mComposerStates) {
if (mComposerStates.count(handle) == 0) {
mComposerStates[handle] = composerState;
} else {
+ if (composerState.state.what & layer_state_t::eBufferChanged) {
+ releaseBufferIfOverwriting(mComposerStates[handle].state);
+ }
mComposerStates[handle].state.merge(composerState.state);
}
}
@@ -792,7 +826,7 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, applyToken, {}, systemTime(), true,
- uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
+ uncacheBuffer, false, {}, generateId());
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -805,7 +839,8 @@ void SurfaceComposerClient::Transaction::cacheBuffers() {
layer_state_t* s = &(mComposerStates[handle].state);
if (!(s->what & layer_state_t::eBufferChanged)) {
continue;
- } else if (s->what & layer_state_t::eCachedBufferChanged) {
+ } else if (s->bufferData &&
+ s->bufferData->flags.test(BufferData::BufferDataChange::cachedBufferChanged)) {
// If eBufferChanged and eCachedBufferChanged are both trued then that means
// we already cached the buffer in a previous call to cacheBuffers, perhaps
// from writeToParcel on a Transaction that was merged in to this one.
@@ -814,23 +849,22 @@ void SurfaceComposerClient::Transaction::cacheBuffers() {
// Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste
// time trying to cache them.
- if (!s->buffer) {
+ if (!s->bufferData || !s->bufferData->buffer) {
continue;
}
uint64_t cacheId = 0;
- status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId);
+ status_t ret = BufferCache::getInstance().getCacheId(s->bufferData->buffer, &cacheId);
if (ret == NO_ERROR) {
// Cache-hit. Strip the buffer and send only the id.
- s->what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
- s->buffer = nullptr;
+ s->bufferData->buffer = nullptr;
} else {
// Cache-miss. Include the buffer and send the new cacheId.
- cacheId = BufferCache::getInstance().cache(s->buffer);
+ cacheId = BufferCache::getInstance().cache(s->bufferData->buffer);
}
- s->what |= layer_state_t::eCachedBufferChanged;
- s->cachedBuffer.token = BufferCache::getInstance().getToken();
- s->cachedBuffer.id = cacheId;
+ s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
+ s->bufferData->cachedBuffer.token = BufferCache::getInstance().getToken();
+ s->bufferData->cachedBuffer.id = cacheId;
// If we have more buffers than the size of the cache, we should stop caching so we don't
// evict other buffers in this transaction
@@ -1071,7 +1105,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags
}
if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
(mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
- (mask & layer_state_t::eEnableBackpressure)) {
+ (mask & layer_state_t::eEnableBackpressure) ||
+ (mask & layer_state_t::eLayerIsDisplayDecoration)) {
s->what |= layer_state_t::eFlagsChanged;
}
s->flags &= ~mask;
@@ -1112,7 +1147,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayerStack(
- const sp<SurfaceControl>& sc, uint32_t layerStack) {
+ const sp<SurfaceControl>& sc, ui::LayerStack layerStack) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1288,73 +1323,78 @@ SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp<Surfac
return *this;
}
+std::shared_ptr<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffer(
+ const sp<SurfaceControl>& sc) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ return nullptr;
+ }
+ if (!(s->what & layer_state_t::eBufferChanged)) {
+ return nullptr;
+ }
+
+ std::shared_ptr<BufferData> bufferData = std::move(s->bufferData);
+
+ TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(
+ bufferData->generateReleaseCallbackId());
+ s->what &= ~layer_state_t::eBufferChanged;
+ s->bufferData = nullptr;
+
+ mContainsBuffer = false;
+ return bufferData;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
- const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, const ReleaseCallbackId& id,
+ const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+ const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
ReleaseBufferCallback callback) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- removeReleaseBufferCallback(s);
- s->what |= layer_state_t::eBufferChanged;
- s->buffer = buffer;
- s->releaseBufferEndpoint = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
+ releaseBufferIfOverwriting(*s);
+
+ std::shared_ptr<BufferData> bufferData = std::make_shared<BufferData>();
+ bufferData->buffer = buffer;
+ if (frameNumber) {
+ bufferData->frameNumber = *frameNumber;
+ bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
+ }
+ if (fence) {
+ bufferData->acquireFence = *fence;
+ bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
+ }
+ bufferData->releaseBufferEndpoint =
+ IInterface::asBinder(TransactionCompletedListener::getIInstance());
if (mIsAutoTimestamp) {
mDesiredPresentTime = systemTime();
}
- setReleaseBufferCallback(s, id, callback);
-
+ setReleaseBufferCallback(bufferData.get(), callback);
+ s->what |= layer_state_t::eBufferChanged;
+ s->bufferData = std::move(bufferData);
registerSurfaceControlForCallback(sc);
mContainsBuffer = true;
return *this;
}
-void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) {
- if (!s->releaseBufferListener) {
- return;
- }
-
- s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
- s->releaseBufferListener = nullptr;
- auto listener = TransactionCompletedListener::getInstance();
- listener->removeReleaseBufferCallback(s->releaseCallbackId);
- s->releaseCallbackId = ReleaseCallbackId::INVALID_ID;
-}
-
-void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
- const ReleaseCallbackId& id,
+void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData,
ReleaseBufferCallback callback) {
if (!callback) {
return;
}
- if (!s->buffer) {
+ if (!bufferData->buffer) {
ALOGW("Transaction::setReleaseBufferCallback"
"ignored trying to set a callback on a null buffer.");
return;
}
- s->what |= layer_state_t::eReleaseBufferListenerChanged;
- s->releaseBufferListener = TransactionCompletedListener::getIInstance();
- s->releaseCallbackId = id;
+ bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance();
auto listener = TransactionCompletedListener::getInstance();
- listener->setReleaseBufferCallback(id, callback);
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
- const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eAcquireFenceChanged;
- s->acquireFence = fence;
-
- registerSurfaceControlForCallback(sc);
- return *this;
+ listener->setReleaseBufferCallback(bufferData->generateReleaseCallbackId(), callback);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace(
@@ -1506,20 +1546,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber(
- const sp<SurfaceControl>& sc, uint64_t frameNumber) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
-
- s->what |= layer_state_t::eFrameNumberChanged;
- s->frameNumber = frameNumber;
-
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
const sp<SurfaceControl>& sc, const WindowInfo& info) {
layer_state_t* s = getLayerState(sc);
@@ -1792,7 +1818,7 @@ status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>
}
void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& token,
- uint32_t layerStack) {
+ ui::LayerStack layerStack) {
DisplayState& s(getDisplayState(token));
s.layerStack = layerStack;
s.what |= DisplayState::eLayerStackChanged;
@@ -2049,6 +2075,29 @@ status_t SurfaceComposerClient::setActiveColorMode(const sp<IBinder>& display,
return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
}
+status_t SurfaceComposerClient::getBootDisplayModeSupport(bool* support) {
+ return ComposerService::getComposerService()->getBootDisplayModeSupport(support);
+}
+
+status_t SurfaceComposerClient::setBootDisplayMode(const sp<IBinder>& display,
+ ui::DisplayModeId displayModeId) {
+ return ComposerService::getComposerService()->setBootDisplayMode(display, displayModeId);
+}
+
+status_t SurfaceComposerClient::clearBootDisplayMode(const sp<IBinder>& display) {
+ return ComposerService::getComposerService()->clearBootDisplayMode(display);
+}
+
+status_t SurfaceComposerClient::getPreferredBootDisplayMode(const sp<IBinder>& display,
+ ui::DisplayModeId* displayModeId) {
+ return ComposerService::getComposerService()->getPreferredBootDisplayMode(display,
+ displayModeId);
+}
+
+status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) {
+ return ComposerService::getComposerService()->setOverrideFrameRate(uid, frameRate);
+}
+
void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
}
@@ -2190,6 +2239,12 @@ status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColo
lightRadius);
}
+bool SurfaceComposerClient::getDisplayDecorationSupport(const sp<IBinder>& displayToken) {
+ bool support = false;
+ ComposerService::getComposerService()->getDisplayDecorationSupport(displayToken, &support);
+ return support;
+}
+
int SurfaceComposerClient::getGPUContextPriority() {
return ComposerService::getComposerService()->getGPUContextPriority();
}
@@ -2216,12 +2271,12 @@ status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
return s->captureDisplay(captureArgs, captureListener);
}
-status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
+status_t ScreenshotClient::captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureDisplay(displayOrLayerStack, captureListener);
+ return s->captureDisplay(displayId, captureListener);
}
status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
@@ -2232,4 +2287,43 @@ status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
return s->captureLayers(captureArgs, captureListener);
}
+// ---------------------------------------------------------------------------------
+
+void ReleaseCallbackThread::addReleaseCallback(const ReleaseCallbackId callbackId,
+ sp<Fence> releaseFence) {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (!mStarted) {
+ mThread = std::thread(&ReleaseCallbackThread::threadMain, this);
+ mStarted = true;
+ }
+
+ mCallbackInfos.emplace(callbackId, std::move(releaseFence));
+ mReleaseCallbackPending.notify_one();
+}
+
+void ReleaseCallbackThread::threadMain() {
+ const auto listener = TransactionCompletedListener::getInstance();
+ std::queue<std::tuple<const ReleaseCallbackId, const sp<Fence>>> callbackInfos;
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ callbackInfos = std::move(mCallbackInfos);
+ mCallbackInfos = {};
+ }
+
+ while (!callbackInfos.empty()) {
+ auto [callbackId, releaseFence] = callbackInfos.front();
+ listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+ callbackInfos.pop();
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mCallbackInfos.size() == 0) {
+ mReleaseCallbackPending.wait(lock);
+ }
+ }
+ }
+}
+
} // namespace android
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index b2ef7aabc9..8d356aaaa1 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -43,6 +43,14 @@ bool WindowInfo::supportsSplitTouch() const {
return flags.test(Flag::SPLIT_TOUCH);
}
+bool WindowInfo::isSpy() const {
+ return inputFeatures.test(Feature::SPY);
+}
+
+bool WindowInfo::interceptsStylus() const {
+ return inputFeatures.test(Feature::INTERCEPTS_STYLUS);
+}
+
bool WindowInfo::overlaps(const WindowInfo* other) const {
return frameLeft < other->frameRight && frameRight > other->frameLeft &&
frameTop < other->frameBottom && frameBottom > other->frameTop;
@@ -54,12 +62,11 @@ bool WindowInfo::operator==(const WindowInfo& info) const {
info.frameLeft == frameLeft && info.frameTop == frameTop &&
info.frameRight == frameRight && info.frameBottom == frameBottom &&
info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
- info.transform == transform && info.displayOrientation == displayOrientation &&
- info.displayWidth == displayWidth && info.displayHeight == displayHeight &&
- info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
- info.trustedOverlay == trustedOverlay && info.focusable == focusable &&
- info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper &&
- info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+ info.visible == visible && info.trustedOverlay == trustedOverlay &&
+ info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+ info.hasWallpaper == hasWallpaper && info.paused == paused &&
+ info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
info.packageName == packageName && info.inputFeatures == inputFeatures &&
info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
@@ -97,9 +104,6 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
parcel->writeFloat(transform.dtdy()) ?:
parcel->writeFloat(transform.dsdy()) ?:
parcel->writeFloat(transform.ty()) ?:
- parcel->writeUint32(displayOrientation) ?:
- parcel->writeInt32(displayWidth) ?:
- parcel->writeInt32(displayHeight) ?:
parcel->writeBool(visible) ?:
parcel->writeBool(focusable) ?:
parcel->writeBool(hasWallpaper) ?:
@@ -155,9 +159,6 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
parcel->readFloat(&dtdy) ?:
parcel->readFloat(&dsdy) ?:
parcel->readFloat(&ty) ?:
- parcel->readUint32(&displayOrientation) ?:
- parcel->readInt32(&displayWidth) ?:
- parcel->readInt32(&displayHeight) ?:
parcel->readBool(&visible) ?:
parcel->readBool(&focusable) ?:
parcel->readBool(&hasWallpaper) ?:
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index c00a4389ad..c32b9ab398 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -19,6 +19,7 @@
namespace android {
+using gui::DisplayInfo;
using gui::IWindowInfosReportedListener;
using gui::WindowInfo;
using gui::WindowInfosListener;
@@ -65,7 +66,7 @@ status_t WindowInfosListenerReporter::removeWindowInfosListener(
}
binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
- const std::vector<WindowInfo>& windowInfos,
+ const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
std::unordered_set<sp<WindowInfosListener>, ISurfaceComposer::SpHash<WindowInfosListener>>
windowInfosListeners;
@@ -78,7 +79,7 @@ binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
}
for (auto listener : windowInfosListeners) {
- listener->onWindowInfosChanged(windowInfos);
+ listener->onWindowInfosChanged(windowInfos, displayInfos);
}
if (windowInfosReportedListener) {
diff --git a/libs/ui/Size.cpp b/libs/gui/aidl/android/gui/BitTube.aidl
index d2996d164d..6b0595ec66 100644
--- a/libs/ui/Size.cpp
+++ b/libs/gui/aidl/android/gui/BitTube.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include <ui/Size.h>
+package android.gui;
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+parcelable BitTube cpp_header "private/gui/BitTube.h";
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
index cff22a368a..9f41593539 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,52 +14,28 @@
* limitations under the License.
*/
-#pragma once
+package android.gui;
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-#include <gui/ISurfaceComposer.h>
-#include <utils/Errors.h>
-
-#include <cstdint>
-
-namespace android {
-
-namespace gui {
-class BitTube;
-} // namespace gui
-
-class IDisplayEventConnection : public IInterface {
-public:
- DECLARE_META_INTERFACE(DisplayEventConnection)
+import android.gui.BitTube;
+/** @hide */
+interface IDisplayEventConnection {
/*
* stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
* descriptor of outChannel will be initialized, and this effectively "steals" the receive
* channel from the remote end (such that the remote end can only use its send channel).
*/
- virtual status_t stealReceiveChannel(gui::BitTube* outChannel) = 0;
+ void stealReceiveChannel(out BitTube outChannel);
/*
* setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
* A value of 2 returns every other event, etc. A value of 0 returns no event unless
* requestNextVsync() has been called.
*/
- virtual status_t setVsyncRate(uint32_t count) = 0;
+ void setVsyncRate(in int count);
/*
* requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
*/
- virtual void requestNextVsync() = 0; // Asynchronous
-};
-
-class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
-public:
- BnDisplayEventConnection()
- : SafeBnInterface<IDisplayEventConnection>("BnDisplayEventConnection") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-} // namespace android
+ oneway void requestNextVsync(); // Asynchronous
+}
diff --git a/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
new file mode 100644
index 0000000000..00a3959d79
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IRegionSamplingListener {
+ void onSampleCollected(float medianLuma);
+}
diff --git a/libs/gui/android/gui/DisplayInfo.aidl b/libs/gui/android/gui/DisplayInfo.aidl
new file mode 100644
index 0000000000..30c088525d
--- /dev/null
+++ b/libs/gui/android/gui/DisplayInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+parcelable DisplayInfo cpp_header "gui/DisplayInfo.h";
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
index d4553ca82d..a5b2762318 100644
--- a/libs/gui/android/gui/IWindowInfosListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -16,11 +16,12 @@
package android.gui;
+import android.gui.DisplayInfo;
import android.gui.IWindowInfosReportedListener;
import android.gui.WindowInfo;
/** @hide */
oneway interface IWindowInfosListener
{
- void onWindowInfosChanged(in WindowInfo[] windowInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+ void onWindowInfosChanged(in WindowInfo[] windowInfos, in DisplayInfo[] displayInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index f9e40ecb5b..f77cfe6a69 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -81,6 +81,7 @@ public:
return mProducer;
}
sp<Surface> getSurface(bool includeSurfaceControlHandle);
+ bool isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const;
void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
void onFrameReplaced(const BufferItem& item) override;
@@ -90,16 +91,13 @@ public:
void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
- void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
- const std::vector<SurfaceControlStats>& stats);
+ virtual void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats);
void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
- void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
- void setNextTransaction(SurfaceComposerClient::Transaction *t);
+ std::optional<uint32_t> currentMaxAcquiredBufferCount);
+ void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true);
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
- void setTransactionCompleteCallback(uint64_t frameNumber,
- std::function<void(int64_t)>&& transactionCompleteCallback);
+ void applyPendingTransactions(uint64_t frameNumber);
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format,
SurfaceComposerClient::Transaction* outTransaction = nullptr);
@@ -110,9 +108,8 @@ public:
void setSidebandStream(const sp<NativeHandle>& stream);
uint32_t getLastTransformHint() const;
- void flushShadowQueue();
-
uint64_t getLastAcquiredFrameNum();
+ void abandon();
virtual ~BLASTBufferQueue();
@@ -132,9 +129,14 @@ private:
bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
static PixelFormat convertBufferFormat(PixelFormat& format);
+ void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
+ REQUIRES(mMutex);
- void flushShadowQueueLocked() REQUIRES(mMutex);
+ void flushShadowQueue() REQUIRES(mMutex);
void acquireAndReleaseBuffer() REQUIRES(mMutex);
+ void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence)
+ REQUIRES(mMutex);
+ void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock);
std::string mName;
// Represents the queued buffer count from buffer queue,
@@ -144,15 +146,15 @@ private:
std::string mQueuedBufferTrace;
sp<SurfaceControl> mSurfaceControl;
- std::mutex mMutex;
+ mutable std::mutex mMutex;
std::condition_variable mCallbackCV;
// BufferQueue internally allows 1 more than
// the max to be acquired
int32_t mMaxAcquiredBuffers = 1;
- int32_t mNumFrameAvailable GUARDED_BY(mMutex);
- int32_t mNumAcquired GUARDED_BY(mMutex);
+ int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0;
+ int32_t mNumAcquired GUARDED_BY(mMutex) = 0;
// Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
// buffer or the buffer has been presented and a new buffer is ready to be presented.
@@ -165,12 +167,6 @@ private:
struct ReleasedBuffer {
ReleaseCallbackId callbackId;
sp<Fence> releaseFence;
- bool operator==(const ReleasedBuffer& rhs) const {
- // Only compare Id so if we somehow got two callbacks
- // with different fences we don't decrement mNumAcquired
- // too far.
- return rhs.callbackId == callbackId;
- }
};
std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex);
@@ -217,7 +213,7 @@ private:
sp<IGraphicBufferProducer> mProducer;
sp<BLASTBufferItemConsumer> mBufferItemConsumer;
- SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+ SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
@@ -231,9 +227,6 @@ private:
// Tracks the last acquired frame number
uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
- std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr;
- uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0};
-
// Queues up transactions using this token in SurfaceFlinger. This prevents queued up
// transactions from other parts of the client from blocking this transaction.
const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder();
@@ -251,7 +244,11 @@ private:
std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
uint32_t mCurrentMaxAcquiredBufferCount;
- bool mWaitForTransactionCallback = false;
+ bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false;
+
+ // Flag to determine if syncTransaction should only acquire a single buffer and then clear or
+ // continue to acquire buffers until explicitly cleared
+ bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 08f3597bc5..40621ddb15 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -17,6 +17,7 @@
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
#include <utils/Looper.h>
+#include <array>
namespace android {
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -33,6 +34,26 @@ struct VsyncEventData {
// The current frame interval in ns when this frame was scheduled.
int64_t frameInterval = 0;
+
+ struct FrameTimeline {
+ // The Vsync Id corresponsing to this vsync event. This will be used to
+ // populate ISurfaceComposer::setFrameTimelineVsync and
+ // SurfaceComposerClient::setFrameTimelineVsync
+ int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
+
+ // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+ // frame by (both on the CPU and the GPU)
+ int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+
+ // The anticipated Vsync present time.
+ int64_t expectedPresentTime = 0;
+ };
+
+ // Sorted possible frame timelines.
+ std::array<FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines;
+
+ // Index into the frameTimelines that represents the platform's preferred frame timeline.
+ size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max();
};
class DisplayEventDispatcher : public LooperCallback {
@@ -76,5 +97,8 @@ private:
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
+
+ void populateFrameTimelines(const DisplayEventReceiver::Event& event,
+ VsyncEventData* outVsyncEventData) const;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0dffbde88a..456bbfb611 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -33,7 +33,7 @@ namespace android {
// ----------------------------------------------------------------------------
-class IDisplayEventConnection;
+using gui::IDisplayEventConnection;
namespace gui {
class BitTube;
@@ -49,6 +49,9 @@ static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) {
// ----------------------------------------------------------------------------
class DisplayEventReceiver {
public:
+ // Max amount of frame timelines is arbitrarily set to be reasonable.
+ static constexpr int64_t kFrameTimelinesLength = 7;
+
enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
@@ -77,6 +80,12 @@ public:
nsecs_t deadlineTimestamp __attribute__((aligned(8)));
nsecs_t frameInterval __attribute__((aligned(8)));
int64_t vsyncId;
+ size_t preferredFrameTimelineIndex __attribute__((aligned(8)));
+ struct FrameTimeline {
+ nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+ nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+ int64_t vsyncId;
+ } frameTimelines[kFrameTimelinesLength];
};
struct Hotplug {
diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h
new file mode 100644
index 0000000000..74f33a2a87
--- /dev/null
+++ b/libs/gui/include/gui/DisplayInfo.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <gui/constants.h>
+#include <ui/Transform.h>
+
+namespace android::gui {
+
+/*
+ * Describes information about a display that can have windows in it.
+ *
+ * This should only be used by InputFlinger to support raw coordinates in logical display space.
+ */
+struct DisplayInfo : public Parcelable {
+ int32_t displayId = ADISPLAY_ID_NONE;
+
+ // Logical display dimensions.
+ int32_t logicalWidth = 0;
+ int32_t logicalHeight = 0;
+
+ // The display transform. This takes display coordinates to logical display coordinates.
+ ui::Transform transform;
+
+ status_t writeToParcel(android::Parcel*) const override;
+
+ status_t readFromParcel(const android::Parcel*) override;
+};
+
+} // namespace android::gui \ No newline at end of file
diff --git a/libs/gui/include/gui/FrameTimelineInfo.h b/libs/gui/include/gui/FrameTimelineInfo.h
index a23c20248c..255ce568d2 100644
--- a/libs/gui/include/gui/FrameTimelineInfo.h
+++ b/libs/gui/include/gui/FrameTimelineInfo.h
@@ -36,6 +36,9 @@ struct FrameTimelineInfo {
// not directly vendor available.
int32_t inputEventId = 0;
+ // The current time in nanoseconds the application started to render the frame.
+ int64_t startTimeNanos = 0;
+
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h
deleted file mode 100644
index 1803d9a1da..0000000000
--- a/libs/gui/include/gui/IRegionSamplingListener.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-
-namespace android {
-
-class IRegionSamplingListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(RegionSamplingListener)
-
- virtual void onSampleCollected(float medianLuma) = 0;
-};
-
-class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> {
-public:
- BnRegionSamplingListener()
- : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index cd289cb401..fb4fb7e2da 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -17,8 +17,10 @@
#pragma once
#include <android/gui/DisplayBrightness.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/IFpsListener.h>
#include <android/gui/IHdrLayerInfoListener.h>
+#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
@@ -60,13 +62,13 @@ struct InputWindowCommands;
struct LayerCaptureArgs;
class LayerDebugInfo;
class HdrCapabilities;
-class IDisplayEventConnection;
class IGraphicBufferProducer;
class ISurfaceComposerClient;
-class IRegionSamplingListener;
class Rect;
enum class FrameEvent;
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using gui::IScreenCaptureListener;
namespace ui {
@@ -116,6 +118,11 @@ public:
using EventRegistrationFlags = Flags<EventRegistration>;
+ template <typename T>
+ struct SpHash {
+ size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
+ };
+
/*
* Create a connection with SurfaceFlinger.
*/
@@ -217,6 +224,35 @@ public:
ui::ColorMode colorMode) = 0;
/**
+ * Sets the user-preferred display mode that a device should boot in.
+ */
+ virtual status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId) = 0;
+
+ /**
+ * Clears the user-preferred display mode. The device should now boot in system preferred
+ * display mode.
+ */
+ virtual status_t clearBootDisplayMode(const sp<IBinder>& display) = 0;
+
+ /**
+ * Gets the display mode in which the device boots if there is no user-preferred display mode.
+ */
+ virtual status_t getPreferredBootDisplayMode(const sp<IBinder>& display,
+ ui::DisplayModeId*) = 0;
+
+ /**
+ * Gets whether boot time display mode operations are supported on the device.
+ *
+ * outSupport
+ * An output parameter for whether boot time display mode operations are supported.
+ *
+ * Returns NO_ERROR upon success. Otherwise,
+ * NAME_NOT_FOUND if the display is invalid, or
+ * BAD_VALUE if the output parameter is invalid.
+ */
+ virtual status_t getBootDisplayModeSupport(bool* outSupport) const = 0;
+
+ /**
* Switches Auto Low Latency Mode on/off on the connected display, if it is
* available. This should only be called if the display supports Auto Low
* Latency Mode as reported in #getDynamicDisplayInfo.
@@ -241,24 +277,17 @@ public:
* The subregion can be optionally rotated. It will also be scaled to
* match the size of the output buffer.
*/
- virtual status_t captureDisplay(const DisplayCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) = 0;
+ virtual status_t captureDisplay(const DisplayCaptureArgs&,
+ const sp<IScreenCaptureListener>&) = 0;
- virtual status_t captureDisplay(uint64_t displayOrLayerStack,
- const sp<IScreenCaptureListener>& captureListener) = 0;
-
- template <class AA>
- struct SpHash {
- size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); }
- };
+ virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0;
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
* This requires READ_FRAME_BUFFER permission. This function will fail if there
* is a secure window on screen
*/
- virtual status_t captureLayers(const LayerCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) = 0;
+ virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0;
/* Clears the frame statistics for animations.
*
@@ -508,18 +537,33 @@ public:
float lightRadius) = 0;
/*
+ * Gets whether a display supports DISPLAY_DECORATION layers.
+ *
+ * displayToken
+ * The token of the display.
+ * outSupport
+ * An output parameter for whether the display supports
+ * DISPLAY_DECORATION layers.
+ *
+ * Returns NO_ERROR upon success. Otherwise,
+ * NAME_NOT_FOUND if the display is invalid, or
+ * BAD_VALUE if the output parameter is invalid.
+ */
+ virtual status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const = 0;
+
+ /*
* Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
*/
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
/*
- * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
- * surface flinger will freely switch between frame rates in any way it sees fit, regardless of
- * the current restrictions applied by DisplayManager. This is useful to get consistent behavior
- * for tests. Release the token by releasing the returned IBinder reference.
+ * Set the override frame rate for a specified uid by GameManagerService.
+ * Passing the frame rate and uid to SurfaceFlinger to update the override mapping
+ * in the scheduler.
*/
- virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
+ virtual status_t setOverrideFrameRate(uid_t uid, float frameRate) = 0;
/*
* Sets the frame timeline vsync info received from choreographer that corresponds to next
@@ -618,6 +662,7 @@ public:
GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
SET_GAME_CONTENT_TYPE,
SET_FRAME_RATE,
+ // Deprecated. Use DisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
SET_FRAME_TIMELINE_INFO,
ADD_TRANSACTION_TRACE_LISTENER,
@@ -635,6 +680,12 @@ public:
ADD_WINDOW_INFOS_LISTENER,
REMOVE_WINDOW_INFOS_LISTENER,
GET_PRIMARY_PHYSICAL_DISPLAY_ID,
+ GET_DISPLAY_DECORATION_SUPPORT,
+ GET_BOOT_DISPLAY_MODE_SUPPORT,
+ SET_BOOT_DISPLAY_MODE,
+ CLEAR_BOOT_DISPLAY_MODE,
+ GET_PREFERRED_BOOT_DISPLAY_MODE,
+ SET_OVERRIDE_FRAME_RATE,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 937095c543..0df5822597 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -192,7 +192,6 @@ public:
virtual void onTransactionCompleted(ListenerStats stats) = 0;
virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t transformHint,
uint32_t currentMaxAcquiredBufferCount) = 0;
};
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index de14b3d785..27f4d379e9 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -59,4 +59,14 @@ struct LayerMetadata : public Parcelable {
std::string itemToString(uint32_t key, const char* separator) const;
};
+// Keep in sync with the GameManager.java constants.
+enum class GameMode : int32_t {
+ Unsupported = 0,
+ Standard = 1,
+ Performance = 2,
+ Battery = 3,
+
+ ftl_last = Battery
+};
+
} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 03e4aacdbe..b3e6ffa908 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -36,6 +36,7 @@
#include <math/vec3.h>
#include <ui/BlurRegion.h>
#include <ui/GraphicTypes.h>
+#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Rotation.h>
@@ -57,6 +58,57 @@ struct client_cache_t {
bool isValid() const { return token != nullptr; }
};
+class BufferData : public Parcelable {
+public:
+ virtual ~BufferData() = default;
+ virtual bool hasBuffer() const { return buffer != nullptr; }
+ virtual bool hasSameBuffer(const BufferData& other) const {
+ return buffer == other.buffer && frameNumber == other.frameNumber;
+ }
+ virtual uint32_t getWidth() const { return buffer->getWidth(); }
+ virtual uint32_t getHeight() const { return buffer->getHeight(); }
+ Rect getBounds() const {
+ return {0, 0, static_cast<int32_t>(getWidth()), static_cast<int32_t>(getHeight())};
+ }
+ virtual uint64_t getId() const { return buffer->getId(); }
+ virtual PixelFormat getPixelFormat() const { return buffer->getPixelFormat(); }
+ virtual uint64_t getUsage() const { return buffer->getUsage(); }
+
+ enum class BufferDataChange : uint32_t {
+ fenceChanged = 0x01,
+ frameNumberChanged = 0x02,
+ cachedBufferChanged = 0x04,
+ };
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> acquireFence;
+
+ // Used by BlastBufferQueue to forward the framenumber generated by the
+ // graphics producer.
+ uint64_t frameNumber = 0;
+
+ // Listens to when the buffer is safe to be released. This is used for blast
+ // layers only. The callback includes a release fence as well as the graphic
+ // buffer id to identify the buffer.
+ sp<ITransactionCompletedListener> releaseBufferListener = nullptr;
+
+ // Stores which endpoint the release information should be sent to. We don't want to send the
+ // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
+ // was called with.
+ sp<IBinder> releaseBufferEndpoint;
+
+ Flags<BufferDataChange> flags;
+
+ client_cache_t cachedBuffer;
+
+ // Generates the release callback id based on the buffer id and frame number.
+ // This is used as an identifier when release callbacks are invoked.
+ ReleaseCallbackId generateReleaseCallbackId() const;
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+};
+
/*
* Used to communicate layer information between SurfaceFlinger and its clients.
*/
@@ -70,6 +122,7 @@ struct layer_state_t {
// set. This blocks the client until all the buffers have been presented. If the buffers
// have presentation timestamps, then we may drop buffers.
eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE
+ eLayerIsDisplayDecoration = 0x200, // DISPLAY_DECORATION
};
enum {
@@ -81,9 +134,9 @@ struct layer_state_t {
eTransparentRegionChanged = 0x00000020,
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
- eReleaseBufferListenerChanged = 0x00000400,
+ /* unused 0x00000400, */
eShadowRadiusChanged = 0x00000800,
- eLayerCreated = 0x00001000,
+ /* unused 0x00001000, */
eBufferCropChanged = 0x00002000,
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
@@ -93,7 +146,7 @@ struct layer_state_t {
eTransformToDisplayInverseChanged = 0x00080000,
eCropChanged = 0x00100000,
eBufferChanged = 0x00200000,
- eAcquireFenceChanged = 0x00400000,
+ /* unused 0x00400000, */
eDataspaceChanged = 0x00800000,
eHdrMetadataChanged = 0x01000000,
eSurfaceDamageRegionChanged = 0x02000000,
@@ -104,7 +157,7 @@ struct layer_state_t {
eInputInfoChanged = 0x40000000,
eCornerRadiusChanged = 0x80000000,
eDestinationFrameChanged = 0x1'00000000,
- eCachedBufferChanged = 0x2'00000000,
+ /* unused = 0x2'00000000, */
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
eColorSpaceAgnosticChanged = 0x10'00000000,
@@ -113,7 +166,7 @@ struct layer_state_t {
eBackgroundBlurRadiusChanged = 0x80'00000000,
eProducerDisconnect = 0x100'00000000,
eFixedTransformHintChanged = 0x200'00000000,
- eFrameNumberChanged = 0x400'00000000,
+ /* unused 0x400'00000000, */
eBlurRegionsChanged = 0x800'00000000,
eAutoRefreshChanged = 0x1000'00000000,
eStretchChanged = 0x2000'00000000,
@@ -145,7 +198,7 @@ struct layer_state_t {
int32_t z;
uint32_t w;
uint32_t h;
- uint32_t layerStack;
+ ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
float alpha;
uint32_t flags;
uint32_t mask;
@@ -167,9 +220,7 @@ struct layer_state_t {
uint32_t transform;
bool transformToDisplayInverse;
Rect crop;
- Rect orientedDisplaySpaceRect;
- sp<GraphicBuffer> buffer;
- sp<Fence> acquireFence;
+ std::shared_ptr<BufferData> bufferData = nullptr;
ui::Dataspace dataspace;
HdrMetadata hdrMetadata;
Region surfaceDamageRegion;
@@ -180,8 +231,6 @@ struct layer_state_t {
sp<gui::WindowInfoHandle> windowInfoHandle = new gui::WindowInfoHandle();
- client_cache_t cachedBuffer;
-
LayerMetadata metadata;
// The following refer to the alpha, and dataspace, respectively of
@@ -215,10 +264,6 @@ struct layer_state_t {
// otherwise the value will be a valid ui::Rotation.
ui::Transform::RotationFlags fixedTransformHint;
- // Used by BlastBufferQueue to forward the framenumber generated by the
- // graphics producer.
- uint64_t frameNumber;
-
// Indicates that the consumer should acquire the next frame as soon as it
// can and not wait for a frame to become available. This is only relevant
// in shared buffer mode.
@@ -234,22 +279,6 @@ struct layer_state_t {
Rect bufferCrop;
Rect destinationFrame;
- // Listens to when the buffer is safe to be released. This is used for blast
- // layers only. The callback includes a release fence as well as the graphic
- // buffer id to identify the buffer.
- sp<ITransactionCompletedListener> releaseBufferListener;
-
- // Keeps track of the release callback id associated with the listener. This
- // is not sent to the server since the id can be reconstructed there. This
- // is used to remove the old callback from the client process map if it is
- // overwritten by another setBuffer call.
- ReleaseCallbackId releaseCallbackId;
-
- // Stores which endpoint the release information should be sent to. We don't want to send the
- // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
- // was called with.
- sp<IBinder> releaseBufferEndpoint;
-
// Force inputflinger to drop all input events for the layer and its children.
gui::DropInputMode dropInputMode;
};
@@ -272,11 +301,12 @@ struct DisplayState {
DisplayState();
void merge(const DisplayState& other);
- uint32_t what;
+ uint32_t what = 0;
+ uint32_t flags = 0;
sp<IBinder> token;
sp<IGraphicBufferProducer> surface;
- uint32_t layerStack;
- uint32_t flags;
+
+ ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
// These states define how layers are projected onto the physical display.
//
@@ -290,10 +320,11 @@ struct DisplayState {
// will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
// 0).
ui::Rotation orientation = ui::ROTATION_0;
- Rect layerStackSpaceRect;
- Rect orientedDisplaySpaceRect;
+ Rect layerStackSpaceRect = Rect::EMPTY_RECT;
+ Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
- uint32_t width, height;
+ uint32_t width = 0;
+ uint32_t height = 0;
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index e5403512a9..40d096e1e5 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -429,11 +429,11 @@ protected:
uint32_t mReqHeight;
// mReqFormat is the buffer pixel format that will be requested at the next
- // deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
+ // dequeue operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
PixelFormat mReqFormat;
// mReqUsage is the set of buffer usage flags that will be requested
- // at the next deuque operation. It is initialized to 0.
+ // at the next dequeue operation. It is initialized to 0.
uint64_t mReqUsage;
// mTimestamp is the timestamp that will be used for the next buffer queue
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0d1d1a37bd..4f928781d9 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,11 +19,13 @@
#include <stdint.h>
#include <sys/types.h>
#include <set>
+#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <binder/IBinder.h>
+#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
#include <utils/SortedVector.h>
@@ -50,22 +52,22 @@ namespace android {
class HdrCapabilities;
class ISurfaceComposerClient;
class IGraphicBufferProducer;
-class IRegionSamplingListener;
class ITunnelModeEnabledListener;
class Region;
+using gui::IRegionSamplingListener;
+
struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
- uint32_t hint, FrameEventHistoryStats eventStats, uint32_t currentMaxAcquiredBufferCount)
+ uint32_t hint, FrameEventHistoryStats eventStats)
: surfaceControl(sc),
latchTime(latchTime),
acquireTime(acquireTime),
presentFence(presentFence),
previousReleaseFence(prevReleaseFence),
transformHint(hint),
- frameEventStats(eventStats),
- currentMaxAcquiredBufferCount(currentMaxAcquiredBufferCount) {}
+ frameEventStats(eventStats) {}
sp<SurfaceControl> surfaceControl;
nsecs_t latchTime = -1;
@@ -74,7 +76,6 @@ struct SurfaceControlStats {
sp<Fence> previousReleaseFence;
uint32_t transformHint = 0;
FrameEventHistoryStats frameEventStats;
- uint32_t currentMaxAcquiredBufferCount = 0;
};
using TransactionCompletedCallbackTakesContext =
@@ -86,7 +87,7 @@ using TransactionCompletedCallback =
const std::vector<SurfaceControlStats>& /*stats*/)>;
using ReleaseBufferCallback =
std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount)>;
+ std::optional<uint32_t> currentMaxAcquiredBufferCount)>;
using SurfaceStatsCallback =
std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
@@ -95,6 +96,22 @@ using SurfaceStatsCallback =
// ---------------------------------------------------------------------------
+class ReleaseCallbackThread {
+public:
+ void addReleaseCallback(const ReleaseCallbackId, sp<Fence>);
+ void threadMain();
+
+private:
+ std::thread mThread;
+ std::mutex mMutex;
+ bool mStarted GUARDED_BY(mMutex) = false;
+ std::condition_variable mReleaseCallbackPending;
+ std::queue<std::tuple<const ReleaseCallbackId, const sp<Fence>>> mCallbackInfos
+ GUARDED_BY(mMutex);
+};
+
+// ---------------------------------------------------------------------------
+
class SurfaceComposerClient : public RefBase
{
friend class Composer;
@@ -151,6 +168,18 @@ public:
static status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode);
+ // Gets if boot display mode operations are supported on a device
+ static status_t getBootDisplayModeSupport(bool* support);
+ // Sets the user-preferred display mode that a device should boot in
+ static status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId);
+ // Clears the user-preferred display mode
+ static status_t clearBootDisplayMode(const sp<IBinder>& display);
+ // Gets the display mode in which the device boots if there is no user-preferred display mode
+ static status_t getPreferredBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId*);
+ // Sets the frame rate of a particular app (uid). This is currently called
+ // by GameManager.
+ static status_t setOverrideFrameRate(uid_t uid, float frameRate);
+
// Switches on/off Auto Low Latency Mode on the connected display. This should only be
// called if the connected display supports Auto Low Latency Mode as reported by
// #getAutoLowLatencyModeSupport
@@ -256,6 +285,16 @@ public:
static status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius);
+ /*
+ * Returns whether a display supports DISPLAY_DECORATION layers.
+ *
+ * displayToken
+ * The token of the display.
+ *
+ * Returns whether a display supports DISPLAY_DECORATION layers.
+ */
+ static bool getDisplayDecorationSupport(const sp<IBinder>& displayToken);
+
// ------------------------------------------------------------------------
// surface creation / destruction
@@ -350,8 +389,7 @@ public:
class Transaction : public Parcelable {
private:
- static std::atomic<uint32_t> idCounter;
- int64_t generateId();
+ void releaseBufferIfOverwriting(const layer_state_t& state);
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
@@ -401,9 +439,7 @@ public:
void cacheBuffers();
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
- void setReleaseBufferCallback(layer_state_t*, const ReleaseCallbackId&,
- ReleaseBufferCallback);
- void removeReleaseBufferCallback(layer_state_t*);
+ void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback);
public:
Transaction();
@@ -459,7 +495,7 @@ public:
int backgroundBlurRadius);
Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
const std::vector<BlurRegion>& regions);
- Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
+ Transaction& setLayerStack(const sp<SurfaceControl>&, ui::LayerStack);
Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
/// Reparents the current layer to the new parent handle. The new parent must not be null.
@@ -475,10 +511,10 @@ public:
Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
bool transformToDisplayInverse);
Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
- const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID,
+ const std::optional<sp<Fence>>& fence = std::nullopt,
+ const std::optional<uint64_t>& frameNumber = std::nullopt,
ReleaseBufferCallback callback = nullptr);
- Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
- Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
+ std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
@@ -503,8 +539,6 @@ public:
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
- // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
- Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber);
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info);
Transaction& setFocusedWindow(const gui::FocusRequest& request);
@@ -570,7 +604,7 @@ public:
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
- void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
+ void setDisplayLayerStack(const sp<IBinder>& token, ui::LayerStack);
void setDisplayFlags(const sp<IBinder>& token, uint32_t flags);
@@ -630,6 +664,9 @@ public:
status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
+protected:
+ ReleaseCallbackThread mReleaseCallbackThread;
+
private:
virtual void onFirstRef();
@@ -642,12 +679,9 @@ private:
class ScreenshotClient {
public:
- static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
- const sp<IScreenCaptureListener>& captureListener);
- static status_t captureDisplay(uint64_t displayOrLayerStack,
- const sp<IScreenCaptureListener>& captureListener);
- static status_t captureLayers(const LayerCaptureArgs& captureArgs,
- const sp<IScreenCaptureListener>& captureListener);
+ static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
+ static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
};
// ---------------------------------------------------------------------------
@@ -733,13 +767,14 @@ public:
void removeSurfaceStatsListener(void* context, void* cookie);
void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
- void removeReleaseBufferCallback(const ReleaseCallbackId&);
// BnTransactionCompletedListener overrides
void onTransactionCompleted(ListenerStats stats) override;
- void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint,
+ void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
uint32_t currentMaxAcquiredBufferCount) override;
+ void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
+
// For Testing Only
static void setInstance(const sp<TransactionCompletedListener>&);
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 4727740ab5..2bfaec8d03 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -17,6 +17,7 @@
#pragma once
#include <android/gui/TouchOcclusionMode.h>
+#include <android/os/IInputConstants.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <ftl/Flags.h>
@@ -71,8 +72,9 @@ struct WindowInfo : public Parcelable {
SLIPPERY = 0x20000000,
LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
- }; // Window types from WindowManager.LayoutParams
+ };
+ // Window types from WindowManager.LayoutParams
enum class Type : int32_t {
UNKNOWN = 0,
FIRST_APPLICATION_WINDOW = 1,
@@ -87,45 +89,66 @@ struct WindowInfo : public Parcelable {
APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
LAST_SUB_WINDOW = 1999,
- FIRST_SYSTEM_WINDOW = 2000,
- STATUS_BAR = FIRST_SYSTEM_WINDOW,
- SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
- PHONE = FIRST_SYSTEM_WINDOW + 2,
- SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
- KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
- TOAST = FIRST_SYSTEM_WINDOW + 5,
- SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
- PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
- SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
- KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
- SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
- INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
- INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
- WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
- STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
- SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
- DRAG = FIRST_SYSTEM_WINDOW + 16,
- STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
- POINTER = FIRST_SYSTEM_WINDOW + 18,
- NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
- VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
- BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
- INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
- NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
- MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
- ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
- DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
- ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
- NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
+
+#define FIRST_SYSTEM_WINDOW_ 2000
+
+ STATUS_BAR = FIRST_SYSTEM_WINDOW_,
+ SEARCH_BAR = FIRST_SYSTEM_WINDOW_ + 1,
+ PHONE = FIRST_SYSTEM_WINDOW_ + 2,
+ SYSTEM_ALERT = FIRST_SYSTEM_WINDOW_ + 3,
+ KEYGUARD = FIRST_SYSTEM_WINDOW_ + 4,
+ TOAST = FIRST_SYSTEM_WINDOW_ + 5,
+ SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 6,
+ PRIORITY_PHONE = FIRST_SYSTEM_WINDOW_ + 7,
+ SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW_ + 8,
+ KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW_ + 9,
+ SYSTEM_ERROR = FIRST_SYSTEM_WINDOW_ + 10,
+ INPUT_METHOD = FIRST_SYSTEM_WINDOW_ + 11,
+ INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW_ + 12,
+ WALLPAPER = FIRST_SYSTEM_WINDOW_ + 13,
+ STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 14,
+ SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 15,
+ DRAG = FIRST_SYSTEM_WINDOW_ + 16,
+ STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW_ + 17,
+ POINTER = FIRST_SYSTEM_WINDOW_ + 18,
+ NAVIGATION_BAR = FIRST_SYSTEM_WINDOW_ + 19,
+ VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW_ + 20,
+ BOOT_PROGRESS = FIRST_SYSTEM_WINDOW_ + 21,
+ INPUT_CONSUMER = FIRST_SYSTEM_WINDOW_ + 22,
+ NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 24,
+ MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 27,
+ ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW_ + 32,
+ DOCK_DIVIDER = FIRST_SYSTEM_WINDOW_ + 34,
+ ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 39,
+ NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW_ + 40,
+
+ FIRST_SYSTEM_WINDOW = FIRST_SYSTEM_WINDOW_,
LAST_SYSTEM_WINDOW = 2999,
+
+#undef FIRST_SYSTEM_WINDOW_
+
+ // Small range to limit LUT size.
+ ftl_first = FIRST_SYSTEM_WINDOW,
+ ftl_last = FIRST_SYSTEM_WINDOW + 15
};
- enum class Feature {
- DISABLE_TOUCH_PAD_GESTURES = 1u << 0,
- NO_INPUT_CHANNEL = 1u << 1,
- DISABLE_USER_ACTIVITY = 1u << 2,
- DROP_INPUT = 1u << 3,
- DROP_INPUT_IF_OBSCURED = 1u << 4,
+ // This is a conversion of os::IInputConstants::InputFeature to an enum backed by an unsigned
+ // type. This indicates that they are flags, so it can be used with ftl/enum.h.
+ enum class Feature : uint32_t {
+ // clang-format off
+ NO_INPUT_CHANNEL =
+ static_cast<uint32_t>(os::IInputConstants::InputFeature::NO_INPUT_CHANNEL),
+ DISABLE_USER_ACTIVITY =
+ static_cast<uint32_t>(os::IInputConstants::InputFeature::DISABLE_USER_ACTIVITY),
+ DROP_INPUT =
+ static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT),
+ DROP_INPUT_IF_OBSCURED =
+ static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT_IF_OBSCURED),
+ SPY =
+ static_cast<uint32_t>(os::IInputConstants::InputFeature::SPY),
+ INTERCEPTS_STYLUS =
+ static_cast<uint32_t>(os::IInputConstants::InputFeature::INTERCEPTS_STYLUS),
+ // clang-format on
};
/* These values are filled in by the WM and passed through SurfaceFlinger
@@ -170,13 +193,6 @@ struct WindowInfo : public Parcelable {
// Transform applied to individual windows.
ui::Transform transform;
- // Display orientation as ui::Transform::RotationFlags. Used for compatibility raw coordinates.
- uint32_t displayOrientation = ui::Transform::ROT_0;
-
- // Display size in its natural rotation. Used to rotate raw coordinates for compatibility.
- int32_t displayWidth = 0;
- int32_t displayHeight = 0;
-
/*
* This is filled in by the WM relative to the frame and then translated
* to absolute coordinates by SurfaceFlinger once the frame is computed.
@@ -211,6 +227,10 @@ struct WindowInfo : public Parcelable {
bool supportsSplitTouch() const;
+ bool isSpy() const;
+
+ bool interceptsStylus() const;
+
bool overlaps(const WindowInfo* other) const;
bool operator==(const WindowInfo& inputChannel) const;
@@ -267,4 +287,4 @@ protected:
WindowInfo mInfo;
};
-} // namespace android::gui \ No newline at end of file
+} // namespace android::gui
diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h
index 8a70b9bb57..a18a498c5e 100644
--- a/libs/gui/include/gui/WindowInfosListener.h
+++ b/libs/gui/include/gui/WindowInfosListener.h
@@ -16,6 +16,7 @@
#pragma once
+#include <gui/DisplayInfo.h>
#include <gui/WindowInfo.h>
#include <utils/RefBase.h>
@@ -23,6 +24,7 @@ namespace android::gui {
class WindowInfosListener : public virtual RefBase {
public:
- virtual void onWindowInfosChanged(const std::vector<WindowInfo>& /*windowInfos*/) = 0;
+ virtual void onWindowInfosChanged(const std::vector<WindowInfo>&,
+ const std::vector<DisplayInfo>&) = 0;
};
} // namespace android::gui \ No newline at end of file
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 7cb96e0a30..157a804264 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -21,7 +21,6 @@
#include <binder/IBinder.h>
#include <gui/ISurfaceComposer.h>
#include <gui/WindowInfosListener.h>
-#include <utils/Mutex.h>
#include <unordered_set>
namespace android {
@@ -30,7 +29,8 @@ class ISurfaceComposer;
class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
public:
static sp<WindowInfosListenerReporter> getInstance();
- binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos,
+ binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>&,
+ const std::vector<gui::DisplayInfo>&,
const sp<gui::IWindowInfosReportedListener>&) override;
status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index d2e04268dc..64032087eb 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -135,7 +135,7 @@ private:
void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
nsecs_t latchTime) const {
const auto& [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
- transformHint, frameEvents, ignore] = surfaceControlStats;
+ transformHint, frameEvents] = surfaceControlStats;
ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
<< "bad acquire time";
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 3d26c3d858..6dd1073879 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -27,6 +27,7 @@ cc_test {
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
+ "DisplayInfo_test.cpp",
"DisplayedContentSampling_test.cpp",
"FillBuffer.cpp",
"GLTest.cpp",
@@ -62,7 +63,7 @@ cc_test {
"libinput",
"libui",
"libutils",
- "libnativewindow"
+ "libnativewindow",
],
header_libs: ["libsurfaceflinger_headers"],
@@ -117,7 +118,7 @@ cc_test {
"libgui",
"libui",
"libutils",
- "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
+ "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
"libpdx_default_transport",
],
@@ -146,5 +147,5 @@ cc_test {
"liblog",
"libui",
"libutils",
- ]
+ ],
}
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index fc7548511d..42a32f3b42 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -66,26 +66,58 @@ private:
int32_t mNumReleased GUARDED_BY(mMutex) = 0;
};
+class TestBLASTBufferQueue : public BLASTBufferQueue {
+public:
+ TestBLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
+ int height, int32_t format)
+ : BLASTBufferQueue(name, surface, width, height, format) {}
+
+ void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) override {
+ BLASTBufferQueue::transactionCallback(latchTime, presentFence, stats);
+ uint64_t frameNumber = stats[0].frameEventStats.frameNumber;
+
+ {
+ std::unique_lock lock{frameNumberMutex};
+ mLastTransactionFrameNumber = frameNumber;
+ mWaitForCallbackCV.notify_all();
+ }
+ }
+
+ void waitForCallback(int64_t frameNumber) {
+ std::unique_lock lock{frameNumberMutex};
+ // Wait until all but one of the submitted buffers have been released.
+ while (mLastTransactionFrameNumber < frameNumber) {
+ mWaitForCallbackCV.wait(lock);
+ }
+ }
+
+private:
+ std::mutex frameNumberMutex;
+ std::condition_variable mWaitForCallbackCV;
+ int64_t mLastTransactionFrameNumber = -1;
+};
+
class BLASTBufferQueueHelper {
public:
BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
- mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height,
- PIXEL_FORMAT_RGBA_8888);
+ mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width,
+ height, PIXEL_FORMAT_RGBA_8888);
}
void update(const sp<SurfaceControl>& sc, int width, int height) {
mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888);
}
- void setNextTransaction(Transaction* next) {
- mBlastBufferQueueAdapter->setNextTransaction(next);
+ void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) {
+ mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer);
}
int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
- Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
+ Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; }
sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
@@ -107,28 +139,17 @@ public:
}
}
- void setTransactionCompleteCallback(int64_t frameNumber) {
- mBlastBufferQueueAdapter->setTransactionCompleteCallback(frameNumber, [&](int64_t frame) {
- std::unique_lock lock{mMutex};
- mLastTransactionCompleteFrameNumber = frame;
- mCallbackCV.notify_all();
- });
+ void waitForCallback(int64_t frameNumber) {
+ mBlastBufferQueueAdapter->waitForCallback(frameNumber);
}
- void waitForCallback(int64_t frameNumber) {
- std::unique_lock lock{mMutex};
- // Wait until all but one of the submitted buffers have been released.
- while (mLastTransactionCompleteFrameNumber < frameNumber) {
- mCallbackCV.wait(lock);
- }
+ void validateNumFramesSubmitted(int64_t numFramesSubmitted) {
+ std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+ ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
}
private:
- sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
-
- std::mutex mMutex;
- std::condition_variable mCallbackCV;
- int64_t mLastTransactionCompleteFrameNumber = -1;
+ sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
};
class BLASTBufferQueueTest : public ::testing::Test {
@@ -152,7 +173,7 @@ protected:
mDisplayToken = mClient->getInternalDisplayToken();
ASSERT_NE(nullptr, mDisplayToken.get());
Transaction t;
- t.setDisplayLayerStack(mDisplayToken, 0);
+ t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
t.apply();
t.clear();
@@ -166,7 +187,7 @@ protected:
mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState,
/*parent*/ nullptr);
- t.setLayerStack(mSurfaceControl, 0)
+ t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
.show(mSurfaceControl)
.setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
@@ -282,7 +303,7 @@ protected:
auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
nullptr, nullptr);
- ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_TRUE(ret == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || ret == NO_ERROR);
ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf));
uint32_t* bufData;
@@ -321,7 +342,7 @@ TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
ASSERT_EQ(mDisplayWidth, adapter.getWidth());
ASSERT_EQ(mDisplayHeight, adapter.getHeight());
- ASSERT_EQ(nullptr, adapter.getNextTransaction());
+ ASSERT_EQ(nullptr, adapter.getSyncTransaction());
}
TEST_F(BLASTBufferQueueTest, Update) {
@@ -342,11 +363,11 @@ TEST_F(BLASTBufferQueueTest, Update) {
ASSERT_EQ(mDisplayHeight / 2, height);
}
-TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
+TEST_F(BLASTBufferQueueTest, SetSyncTransaction) {
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
- Transaction next;
- adapter.setNextTransaction(&next);
- ASSERT_EQ(&next, adapter.getNextTransaction());
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
+ ASSERT_EQ(&sync, adapter.getSyncTransaction());
}
TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
@@ -516,7 +537,7 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
ISurfaceComposerClient::eFXSurfaceEffect);
ASSERT_NE(nullptr, bg.get());
Transaction t;
- t.setLayerStack(bg, 0)
+ t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
.setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setColor(bg, half3{0, 0, 0})
.setLayer(bg, 0)
@@ -571,7 +592,7 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
ISurfaceComposerClient::eFXSurfaceEffect);
ASSERT_NE(nullptr, bg.get());
Transaction t;
- t.setLayerStack(bg, 0)
+ t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
.setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setColor(bg, half3{0, 0, 0})
.setLayer(bg, 0)
@@ -638,7 +659,7 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
ISurfaceComposerClient::eFXSurfaceEffect);
ASSERT_NE(nullptr, bg.get());
Transaction t;
- t.setLayerStack(bg, 0)
+ t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
.setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setColor(bg, half3{0, 0, 0})
.setLayer(bg, 0)
@@ -785,8 +806,8 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) {
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
- Transaction next;
- adapter.setNextTransaction(&next);
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
// queue non sync buffer, so this one should get blocked
@@ -795,14 +816,14 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) {
queueBuffer(igbProducer, r, g, b, presentTimeDelay);
CallbackHelper transactionCallback;
- next.addTransactionCompletedCallback(transactionCallback.function,
+ sync.addTransactionCompletedCallback(transactionCallback.function,
transactionCallback.getContext())
.apply();
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
- // capture screen and verify that it is red
+ // capture screen and verify that it is green
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -825,16 +846,16 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) {
Transaction mainTransaction;
- Transaction next;
- adapter.setNextTransaction(&next);
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 1 buffer to be released even before sending to SurfaceFlinger
mProducerListener->waitOnNumberReleased(1);
@@ -865,24 +886,24 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) {
Transaction mainTransaction;
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue another buffer without setting next transaction
+ // queue another buffer without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 1 buffer to be released because the non sync transaction should merge
// with the sync
mProducerListener->waitOnNumberReleased(1);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 2 buffers to be released due to merging the two syncs.
mProducerListener->waitOnNumberReleased(2);
@@ -913,26 +934,26 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) {
Transaction mainTransaction;
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue a few buffers without setting next transaction
+ // queue a few buffers without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 3 buffers to be released because the non sync transactions should merge
// with the sync
mProducerListener->waitOnNumberReleased(3);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 4 buffers to be released due to merging the two syncs.
mProducerListener->waitOnNumberReleased(4);
@@ -970,14 +991,14 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) {
// Send a buffer to SF
queueBuffer(igbProducer, 0, 255, 0, 0);
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue a few buffers without setting next transaction
+ // queue a few buffers without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
@@ -986,13 +1007,13 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) {
mainTransaction.apply();
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 2 buffers to be released because the non sync transactions should merge
// with the sync
mProducerListener->waitOnNumberReleased(3);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
CallbackHelper transactionCallback;
mainTransaction
@@ -1009,6 +1030,133 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) {
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
+TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction next;
+ adapter.setSyncTransaction(&next, false);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ // There should only be one frame submitted since the first frame will be released.
+ adapter.validateNumFramesSubmitted(1);
+ adapter.setSyncTransaction(nullptr);
+
+ // queue non sync buffer, so this one should get blocked
+ // Add a present delay to allow the first screenshot to get taken.
+ nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+ queueBuffer(igbProducer, 255, 0, 0, presentTimeDelay);
+
+ CallbackHelper transactionCallback;
+ next.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+
+ // capture screen and verify that it is blue
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+
+ mProducerListener->waitOnNumberReleased(2);
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+// This test will currently fail because the old surfacecontrol will steal the last presented buffer
+// until the old surface control is destroyed. This is not necessarily a bug but to document a
+// limitation with the update API and to test any changes to make the api more robust. The current
+// approach for the client is to recreate the blastbufferqueue when the surfacecontrol updates.
+TEST_F(BLASTBufferQueueTest, DISABLED_DisconnectProducerTest) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ std::vector<sp<SurfaceControl>> surfaceControls;
+ sp<IGraphicBufferProducer> igbProducer;
+ for (int i = 0; i < 10; i++) {
+ sp<SurfaceControl> sc =
+ mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ nullptr);
+ Transaction()
+ .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
+ .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+ .show(mSurfaceControl)
+ .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+ .apply(true);
+ surfaceControls.push_back(sc);
+ adapter.update(sc, mDisplayWidth, mDisplayHeight);
+
+ setUpProducer(adapter, igbProducer);
+ Transaction next;
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ adapter.setSyncTransaction(&next, false);
+ queueBuffer(igbProducer, 255, 0, 0, 0);
+
+ CallbackHelper transactionCallback;
+ next.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+ igbProducer->disconnect(NATIVE_WINDOW_API_CPU);
+ }
+}
+
+// See DISABLED_DisconnectProducerTest
+TEST_F(BLASTBufferQueueTest, DISABLED_UpdateSurfaceControlTest) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ std::vector<sp<SurfaceControl>> surfaceControls;
+ sp<IGraphicBufferProducer> igbProducer;
+ for (int i = 0; i < 10; i++) {
+ sp<SurfaceControl> sc =
+ mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ nullptr);
+ Transaction()
+ .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
+ .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+ .show(mSurfaceControl)
+ .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+ .apply(true);
+ surfaceControls.push_back(sc);
+ adapter.update(sc, mDisplayWidth, mDisplayHeight);
+ setUpProducer(adapter, igbProducer);
+
+ Transaction next;
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ adapter.setSyncTransaction(&next, false);
+ queueBuffer(igbProducer, 255, 0, 0, 0);
+
+ CallbackHelper transactionCallback;
+ next.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+ }
+}
+
class TestProducerListener : public BnProducerListener {
public:
sp<IGraphicBufferProducer> mIgbp;
@@ -1070,7 +1218,7 @@ TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {
ISurfaceComposerClient::eFXSurfaceBufferState);
ASSERT_NE(nullptr, bgSurface.get());
Transaction t;
- t.setLayerStack(bgSurface, 0)
+ t.setLayerStack(bgSurface, ui::DEFAULT_LAYER_STACK)
.show(bgSurface)
.setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
.setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
@@ -1366,7 +1514,6 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) {
IGraphicBufferProducer::QueueBufferOutput qbOutput;
nsecs_t requestedPresentTimeA = 0;
nsecs_t postedTimeA = 0;
- adapter.setTransactionCompleteCallback(1);
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
@@ -1435,7 +1582,6 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) {
// queue another buffer so the first can be dropped
nsecs_t requestedPresentTimeB = 0;
nsecs_t postedTimeB = 0;
- adapter.setTransactionCompleteCallback(2);
presentTime = systemTime() + std::chrono::nanoseconds(1ms).count();
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true,
presentTime);
@@ -1501,7 +1647,6 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_CompositorTimings) {
IGraphicBufferProducer::QueueBufferOutput qbOutput;
nsecs_t requestedPresentTimeA = 0;
nsecs_t postedTimeA = 0;
- adapter.setTransactionCompleteCallback(1);
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
adapter.waitForCallback(1);
diff --git a/libs/gui/tests/DisplayInfo_test.cpp b/libs/gui/tests/DisplayInfo_test.cpp
new file mode 100644
index 0000000000..df3329cd52
--- /dev/null
+++ b/libs/gui/tests/DisplayInfo_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+
+#include <gui/DisplayInfo.h>
+
+namespace android {
+
+using gui::DisplayInfo;
+
+namespace test {
+
+TEST(DisplayInfo, Parcelling) {
+ DisplayInfo info;
+ info.displayId = 42;
+ info.logicalWidth = 99;
+ info.logicalHeight = 78;
+ info.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
+
+ Parcel p;
+ info.writeToParcel(&p);
+ p.setDataPosition(0);
+
+ DisplayInfo info2;
+ info2.readFromParcel(&p);
+ ASSERT_EQ(info.displayId, info2.displayId);
+ ASSERT_EQ(info.logicalWidth, info2.logicalWidth);
+ ASSERT_EQ(info.logicalHeight, info2.logicalHeight);
+ ASSERT_EQ(info.transform, info2.transform);
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 5daef0df28..6f1263bb89 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -46,6 +46,8 @@
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <private/android_filesystem_config.h>
+
using android::os::IInputFlinger;
using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -179,6 +181,25 @@ public:
EXPECT_EQ(flags, mev->getFlags() & flags);
}
+ void expectTapInDisplayCoordinates(int displayX, int displayY) {
+ InputEvent *ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+ MotionEvent *mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+ const PointerCoords &coords = *mev->getRawPointerCoords(0 /*pointerIndex*/);
+ EXPECT_EQ(displayX, coords.getX());
+ EXPECT_EQ(displayY, coords.getY());
+ EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
+
+ ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+ mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
+ }
+
void expectKey(uint32_t keycode) {
InputEvent *ev = consumeEvent();
ASSERT_NE(ev, nullptr);
@@ -220,7 +241,7 @@ public:
t.apply(true);
}
- void requestFocus() {
+ void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
SurfaceComposerClient::Transaction t;
FocusRequest request;
request.token = mInputInfo.token;
@@ -228,7 +249,7 @@ public:
request.focusedToken = nullptr;
request.focusedWindowName = "";
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- request.displayId = 0;
+ request.displayId = displayId;
t.setFocusedWindow(request);
t.apply(true);
}
@@ -255,11 +276,6 @@ private:
mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
- // TODO: Fill in from SF?
- mInputInfo.ownerPid = 11111;
- mInputInfo.ownerUid = 11111;
- mInputInfo.displayId = 0;
-
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
@@ -373,23 +389,33 @@ public:
int32_t mBufferPostDelay;
};
-void injectTap(int x, int y) {
- char *buf1, *buf2;
+void injectTapOnDisplay(int x, int y, int displayId) {
+ char *buf1, *buf2, *bufDisplayId;
asprintf(&buf1, "%d", x);
asprintf(&buf2, "%d", y);
+ asprintf(&bufDisplayId, "%d", displayId);
if (fork() == 0) {
- execlp("input", "input", "tap", buf1, buf2, NULL);
+ execlp("input", "input", "-d", bufDisplayId, "tap", buf1, buf2, NULL);
}
}
-void injectKey(uint32_t keycode) {
- char *buf1;
+void injectTap(int x, int y) {
+ injectTapOnDisplay(x, y, ADISPLAY_ID_DEFAULT);
+}
+
+void injectKeyOnDisplay(uint32_t keycode, int displayId) {
+ char *buf1, *bufDisplayId;
asprintf(&buf1, "%d", keycode);
+ asprintf(&bufDisplayId, "%d", displayId);
if (fork() == 0) {
- execlp("input", "input", "keyevent", buf1, NULL);
+ execlp("input", "input", "-d", bufDisplayId, "keyevent", buf1, NULL);
}
}
+void injectKey(uint32_t keycode) {
+ injectKeyOnDisplay(keycode, ADISPLAY_ID_NONE);
+}
+
TEST_F(InputSurfacesTest, can_receive_input) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
@@ -564,7 +590,7 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) {
bufferSurface->expectTap(1, 1);
}
-TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) {
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<BlastInputSurface> bufferSurface =
BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
@@ -579,7 +605,7 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) {
bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
injectTap(11, 11);
- bufferSurface->expectTap(1, 1);
+ bgSurface->expectTap(1, 1);
}
TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) {
@@ -960,4 +986,145 @@ TEST_F(InputSurfacesTest, drop_input_policy) {
injectKey(AKEYCODE_V);
EXPECT_EQ(surface->consumeEvent(100), nullptr);
}
+
+class MultiDisplayTests : public InputSurfacesTest {
+public:
+ MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
+ void TearDown() override {
+ for (auto &token : mVirtualDisplays) {
+ SurfaceComposerClient::destroyDisplay(token);
+ }
+ InputSurfacesTest::TearDown();
+ }
+
+ void createDisplay(int32_t width, int32_t height, bool isSecure, ui::LayerStack layerStack,
+ bool receivesInput = true, int32_t offsetX = 0, int32_t offsetY = 0) {
+ sp<IGraphicBufferConsumer> consumer;
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(width, height);
+ mProducers.push_back(producer);
+
+ std::string name = "VirtualDisplay";
+ name += std::to_string(mVirtualDisplays.size());
+ sp<IBinder> token = SurfaceComposerClient::createDisplay(String8(name.c_str()), isSecure);
+ SurfaceComposerClient::Transaction t;
+ t.setDisplaySurface(token, producer);
+ t.setDisplayFlags(token, receivesInput ? 0x01 /* DisplayDevice::eReceivesInput */ : 0);
+ t.setDisplayLayerStack(token, layerStack);
+ t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height},
+ {offsetX, offsetY, offsetX + width, offsetY + height});
+ t.apply(true);
+
+ mVirtualDisplays.push_back(token);
+ }
+
+ std::vector<sp<IBinder>> mVirtualDisplays;
+ std::vector<sp<IGraphicBufferProducer>> mProducers;
+};
+
+TEST_F(MultiDisplayTests, drop_input_if_layer_on_invalid_display) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+ // Do not create a display associated with the LayerStack.
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
+ surface->showAt(100, 100);
+
+ injectTapOnDisplay(101, 101, layerStack.id);
+ surface->requestFocus(layerStack.id);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(MultiDisplayTests, virtual_display_receives_input) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+ createDisplay(1000, 1000, false /*isSecure*/, layerStack);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
+ surface->showAt(100, 100);
+
+ injectTapOnDisplay(101, 101, layerStack.id);
+ surface->expectTap(1, 1);
+
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ surface->expectKey(AKEYCODE_V);
+}
+
+/**
+ * When multiple DisplayDevices are mapped to the same layerStack, use the configuration for the
+ * display that can receive input.
+ */
+TEST_F(MultiDisplayTests, many_to_one_display_mapping) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+ createDisplay(1000, 1000, false /*isSecure*/, layerStack, false /*receivesInput*/,
+ 100 /*offsetX*/, 100 /*offsetY*/);
+ createDisplay(1000, 1000, false /*isSecure*/, layerStack, true /*receivesInput*/,
+ 200 /*offsetX*/, 200 /*offsetY*/);
+ createDisplay(1000, 1000, false /*isSecure*/, layerStack, false /*receivesInput*/,
+ 300 /*offsetX*/, 300 /*offsetY*/);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
+ surface->showAt(10, 10);
+
+ // Input injection happens in logical display coordinates.
+ injectTapOnDisplay(11, 11, layerStack.id);
+ // Expect that the display transform for the display that receives input was used.
+ surface->expectTapInDisplayCoordinates(211, 211);
+
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+}
+
+TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+ createDisplay(1000, 1000, false /*isSecure*/, layerStack);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+ t.setLayerStack(sc, layerStack);
+ });
+ surface->showAt(100, 100);
+
+ injectTapOnDisplay(101, 101, layerStack.id);
+
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+
+ // Create the secure display as system, because only certain users can create secure displays.
+ seteuid(AID_SYSTEM);
+ createDisplay(1000, 1000, true /*isSecure*/, layerStack);
+ // Change the uid back to root.
+ seteuid(AID_ROOT);
+
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+ t.setLayerStack(sc, layerStack);
+ });
+ surface->showAt(100, 100);
+
+ injectTapOnDisplay(101, 101, layerStack.id);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+
+ surface->expectKey(AKEYCODE_V);
+}
+
} // namespace android::test
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index 6746b0a827..c9106bed4c 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -17,9 +17,9 @@
#include <gtest/gtest.h>
#include <thread>
+#include <android/gui/BnRegionSamplingListener.h>
#include <binder/ProcessState.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -135,12 +135,13 @@ private:
std::atomic<bool> poll_{true};
};
-struct Listener : BnRegionSamplingListener {
- void onSampleCollected(float medianLuma) override {
+struct Listener : android::gui::BnRegionSamplingListener {
+ binder::Status onSampleCollected(float medianLuma) override {
std::unique_lock<decltype(mutex)> lk(mutex);
received = true;
mLuma = medianLuma;
cv.notify_all();
+ return binder::Status::ok();
};
bool wait_event(std::chrono::milliseconds timeout) {
std::unique_lock<decltype(mutex)> lk(mutex);
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 0cd150d3cb..a083a228a6 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -20,9 +20,9 @@
#include <chrono>
#include <thread>
+#include <android/gui/BnRegionSamplingListener.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
@@ -33,7 +33,7 @@ using namespace std::chrono_literals;
namespace android {
-class Button : public BnRegionSamplingListener {
+class Button : public gui::BnRegionSamplingListener {
public:
Button(const char* name, const Rect& samplingArea) {
sp<SurfaceComposerClient> client = new SurfaceComposerClient;
@@ -99,9 +99,10 @@ private:
.apply();
}
- void onSampleCollected(float medianLuma) override {
+ binder::Status onSampleCollected(float medianLuma) override {
ATRACE_CALL();
setColor(medianLuma);
+ return binder::Status::ok();
}
sp<SurfaceComposerClient> mClient;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index c745505038..0ebd11cf32 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -19,11 +19,11 @@
#include <gtest/gtest.h>
#include <SurfaceFlingerProperties.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <binder/ProcessState.h>
#include <configstore/Utils.h>
#include <gui/BufferItemConsumer.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
@@ -31,9 +31,11 @@
#include <gui/SyncScreenCaptureListener.h>
#include <inttypes.h>
#include <private/gui/ComposerService.h>
+#include <sys/types.h>
#include <ui/BufferQueueDefs.h>
#include <ui/DisplayMode.h>
#include <ui/Rect.h>
+#include <utils/Errors.h>
#include <utils/String8.h>
#include <limits>
@@ -45,6 +47,8 @@ using namespace std::chrono_literals;
// retrieve wide-color and hdr settings from configstore
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using ui::ColorMode;
using Transaction = SurfaceComposerClient::Transaction;
@@ -753,21 +757,28 @@ public:
}
status_t setActiveColorMode(const sp<IBinder>& /*display*/,
ColorMode /*colorMode*/) override { return NO_ERROR; }
- status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
+ status_t getBootDisplayModeSupport(bool* /*outSupport*/) const override { return NO_ERROR; }
+ status_t setBootDisplayMode(const sp<IBinder>& /*display*/, ui::DisplayModeId /*id*/) override {
+ return NO_ERROR;
+ }
+ status_t clearBootDisplayMode(const sp<IBinder>& /*display*/) override { return NO_ERROR; }
+ status_t getPreferredBootDisplayMode(const sp<IBinder>& /*display*/,
+ ui::DisplayModeId* /*id*/) override {
return NO_ERROR;
}
void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
- status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
+
+ status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override {
return NO_ERROR;
}
- virtual status_t captureLayers(
- const LayerCaptureArgs& /* captureArgs */,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
+ status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override {
return NO_ERROR;
}
+ status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override {
+ return NO_ERROR;
+ }
+
status_t clearAnimationFrameStats() override { return NO_ERROR; }
status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
return NO_ERROR;
@@ -882,12 +893,13 @@ public:
return NO_ERROR;
}
- status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
- int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
+ status_t getDisplayDecorationSupport(const sp<IBinder>& /*displayToken*/,
+ bool* /*outSupport*/) const override {
return NO_ERROR;
}
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override {
+ status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
+ int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
return NO_ERROR;
}
@@ -915,6 +927,8 @@ public:
return NO_ERROR;
}
+ status_t setOverrideFrameRate(uid_t /*uid*/, float /*frameRate*/) override { return NO_ERROR; }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index a4f436cdba..dcdf76fe35 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -60,9 +60,6 @@ TEST(WindowInfo, Parcelling) {
i.globalScaleFactor = 0.3;
i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
- i.displayOrientation = ui::Transform::ROT_0;
- i.displayWidth = 1000;
- i.displayHeight = 2000;
i.visible = false;
i.focusable = false;
i.hasWallpaper = false;
@@ -100,8 +97,6 @@ TEST(WindowInfo, Parcelling) {
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
ASSERT_EQ(i.alpha, i2.alpha);
ASSERT_EQ(i.transform, i2.transform);
- ASSERT_EQ(i.displayWidth, i2.displayWidth);
- ASSERT_EQ(i.displayHeight, i2.displayHeight);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.focusable, i2.focusable);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 5f440b77e2..3073d94dbe 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,12 +20,11 @@
#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
#include <inttypes.h>
-#include <limits.h>
#include <string.h>
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <gui/constants.h>
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
@@ -43,15 +42,6 @@ namespace android {
namespace {
-// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
-// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-bool isPerWindowInputRotationEnabled() {
- static const bool PER_WINDOW_INPUT_ROTATION =
- base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
-
- return PER_WINDOW_INPUT_ROTATION;
-}
-
float transformAngle(const ui::Transform& transform, float angleRadians) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
@@ -71,40 +61,17 @@ float transformAngle(const ui::Transform& transform, float angleRadians) {
return atan2f(transformedPoint.x, -transformedPoint.y);
}
-// Rotates the given point to the specified orientation. If the display width and height are
-// provided, the point is rotated in the screen space. Otherwise, the point is rotated about the
-// origin. This helper is used to avoid the extra overhead of creating new Transforms.
-vec2 rotatePoint(uint32_t orientation, float x, float y, int32_t displayWidth = 0,
- int32_t displayHeight = 0) {
- if (orientation == ui::Transform::ROT_0) {
- return {x, y};
- }
-
- vec2 xy(x, y);
- if (orientation == ui::Transform::ROT_90) {
- xy.x = displayHeight - y;
- xy.y = x;
- } else if (orientation == ui::Transform::ROT_180) {
- xy.x = displayWidth - x;
- xy.y = displayHeight - y;
- } else if (orientation == ui::Transform::ROT_270) {
- xy.x = y;
- xy.y = displayWidth - x;
- }
- return xy;
+bool shouldDisregardTransformation(uint32_t source) {
+ // Do not apply any transformations to axes from joysticks or touchpads.
+ return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
+ isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
}
-vec2 applyTransformWithoutTranslation(const ui::Transform& transform, float x, float y) {
- const vec2 transformedXy = transform.transform(x, y);
- const vec2 transformedOrigin = transform.transform(0, 0);
- return transformedXy - transformedOrigin;
-}
-
-bool shouldDisregardWindowTranslation(uint32_t source) {
+bool shouldDisregardOffset(uint32_t source) {
// Pointer events are the only type of events that refer to absolute coordinates on the display,
// so we should apply the entire window transform. For other types of events, we should make
// sure to not apply the window translation/offset.
- return (source & AINPUT_SOURCE_CLASS_POINTER) == 0;
+ return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER);
}
} // namespace
@@ -148,6 +115,12 @@ int32_t IdGenerator::nextId() const {
// --- InputEvent ---
+vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
+ const vec2 transformedXy = transform.transform(xy);
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ return transformedXy - transformedOrigin;
+}
+
const char* inputEventTypeToString(int32_t type) {
switch (type) {
case AINPUT_EVENT_TYPE_KEY: {
@@ -165,16 +138,62 @@ const char* inputEventTypeToString(int32_t type) {
case AINPUT_EVENT_TYPE_DRAG: {
return "DRAG";
}
+ case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+ return "TOUCH_MODE";
+ }
}
return "UNKNOWN";
}
+std::string inputEventSourceToString(int32_t source) {
+ if (source == AINPUT_SOURCE_UNKNOWN) {
+ return "UNKNOWN";
+ }
+ if (source == static_cast<int32_t>(AINPUT_SOURCE_ANY)) {
+ return "ANY";
+ }
+ static const std::map<int32_t, const char*> SOURCES{
+ {AINPUT_SOURCE_KEYBOARD, "KEYBOARD"},
+ {AINPUT_SOURCE_DPAD, "DPAD"},
+ {AINPUT_SOURCE_GAMEPAD, "GAMEPAD"},
+ {AINPUT_SOURCE_TOUCHSCREEN, "TOUCHSCREEN"},
+ {AINPUT_SOURCE_MOUSE, "MOUSE"},
+ {AINPUT_SOURCE_STYLUS, "STYLUS"},
+ {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BLUETOOTH_STYLUS"},
+ {AINPUT_SOURCE_TRACKBALL, "TRACKBALL"},
+ {AINPUT_SOURCE_MOUSE_RELATIVE, "MOUSE_RELATIVE"},
+ {AINPUT_SOURCE_TOUCHPAD, "TOUCHPAD"},
+ {AINPUT_SOURCE_TOUCH_NAVIGATION, "TOUCH_NAVIGATION"},
+ {AINPUT_SOURCE_JOYSTICK, "JOYSTICK"},
+ {AINPUT_SOURCE_HDMI, "HDMI"},
+ {AINPUT_SOURCE_SENSOR, "SENSOR"},
+ {AINPUT_SOURCE_ROTARY_ENCODER, "ROTARY_ENCODER"},
+ };
+ std::string result;
+ for (const auto& [source_entry, str] : SOURCES) {
+ if ((source & source_entry) == source_entry) {
+ if (!result.empty()) {
+ result += " | ";
+ }
+ result += str;
+ }
+ }
+ if (result.empty()) {
+ result = StringPrintf("0x%08x", source);
+ }
+ return result;
+}
+
+bool isFromSource(uint32_t source, uint32_t test) {
+ return (source & test) == test;
+}
+
VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
event.getSource(), event.getDisplayId()},
event.getAction(),
- event.getDownTime(),
event.getFlags() & VERIFIED_KEY_EVENT_FLAGS,
+ event.getDownTime(),
event.getKeyCode(),
event.getScanCode(),
event.getMetaState(),
@@ -187,8 +206,8 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event)
event.getRawX(0),
event.getRawY(0),
event.getActionMasked(),
- event.getDownTime(),
event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS,
+ event.getDownTime(),
event.getMetaState(),
event.getButtonState()};
}
@@ -325,15 +344,6 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win
scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale);
}
-void PointerCoords::scale(float globalScaleFactor) {
- scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
-}
-
-void PointerCoords::applyOffset(float xOffset, float yOffset) {
- setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset);
- setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
-}
-
#ifdef __linux__
status_t PointerCoords::readFromParcel(Parcel* parcel) {
bits = parcel->readInt64();
@@ -427,8 +437,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3
int32_t buttonState, MotionClassification classification,
const ui::Transform& transform, float xPrecision, float yPrecision,
float rawXCursorPosition, float rawYCursorPosition,
- uint32_t displayOrientation, int32_t displayWidth,
- int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+ const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
size_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputEvent::initialize(id, deviceId, source, displayId, hmac);
@@ -444,12 +453,11 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3
mYPrecision = yPrecision;
mRawXCursorPosition = rawXCursorPosition;
mRawYCursorPosition = rawYCursorPosition;
- mDisplayOrientation = displayOrientation;
- mDisplayWidth = displayWidth;
- mDisplayHeight = displayHeight;
+ mRawTransform = rawTransform;
mDownTime = downTime;
mPointerProperties.clear();
- mPointerProperties.appendArray(pointerProperties, pointerCount);
+ mPointerProperties.insert(mPointerProperties.end(), &pointerProperties[0],
+ &pointerProperties[pointerCount]);
mSampleEventTimes.clear();
mSamplePointerCoords.clear();
addSample(eventTime, pointerCoords);
@@ -470,9 +478,7 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
mYPrecision = other->mYPrecision;
mRawXCursorPosition = other->mRawXCursorPosition;
mRawYCursorPosition = other->mRawYCursorPosition;
- mDisplayOrientation = other->mDisplayOrientation;
- mDisplayWidth = other->mDisplayWidth;
- mDisplayHeight = other->mDisplayHeight;
+ mRawTransform = other->mRawTransform;
mDownTime = other->mDownTime;
mPointerProperties = other->mPointerProperties;
@@ -485,8 +491,10 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
mSamplePointerCoords.clear();
size_t pointerCount = other->getPointerCount();
size_t historySize = other->getHistorySize();
- mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array()
- + (historySize * pointerCount), pointerCount);
+ mSamplePointerCoords
+ .insert(mSamplePointerCoords.end(),
+ &other->mSamplePointerCoords[historySize * pointerCount],
+ &other->mSamplePointerCoords[historySize * pointerCount + pointerCount]);
}
}
@@ -494,7 +502,26 @@ void MotionEvent::addSample(
int64_t eventTime,
const PointerCoords* pointerCoords) {
mSampleEventTimes.push_back(eventTime);
- mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
+ mSamplePointerCoords.insert(mSamplePointerCoords.end(), &pointerCoords[0],
+ &pointerCoords[getPointerCount()]);
+}
+
+int MotionEvent::getSurfaceRotation() const {
+ // The surface rotation is the rotation from the window's coordinate space to that of the
+ // display. Since the event's transform takes display space coordinates to window space, the
+ // returned surface rotation is the inverse of the rotation for the surface.
+ switch (mTransform.getOrientation()) {
+ case ui::Transform::ROT_0:
+ return DISPLAY_ORIENTATION_0;
+ case ui::Transform::ROT_90:
+ return DISPLAY_ORIENTATION_270;
+ case ui::Transform::ROT_180:
+ return DISPLAY_ORIENTATION_180;
+ case ui::Transform::ROT_270:
+ return DISPLAY_ORIENTATION_90;
+ default:
+ return -1;
+ }
}
float MotionEvent::getXCursorPosition() const {
@@ -533,62 +560,20 @@ const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-
- if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis);
-
- if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- // For compatibility, convert raw coordinates into "oriented screen space". Once app
- // developers are educated about getRaw, we can consider removing this.
- const vec2 xy = shouldDisregardWindowTranslation(mSource)
- ? rotatePoint(mDisplayOrientation, coords->getX(), coords->getY())
- : rotatePoint(mDisplayOrientation, coords->getX(), coords->getY(), mDisplayWidth,
- mDisplayHeight);
- static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
- return xy[axis];
- }
-
- if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
- // For compatibility, since we convert raw coordinates into "oriented screen space", we
- // need to convert the relative axes into the same orientation for consistency.
- const vec2 relativeXy = rotatePoint(mDisplayOrientation,
- coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
- coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
- return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
- }
-
- return coords->getAxisValue(axis);
+ const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
+ return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords);
}
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-
- if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- const vec2 xy = shouldDisregardWindowTranslation(mSource)
- ? applyTransformWithoutTranslation(mTransform, coords->getX(), coords->getY())
- : mTransform.transform(coords->getXYValue());
- static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
- return xy[axis];
- }
-
- if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
- const vec2 relativeXy =
- applyTransformWithoutTranslation(mTransform,
- coords->getAxisValue(
- AMOTION_EVENT_AXIS_RELATIVE_X),
- coords->getAxisValue(
- AMOTION_EVENT_AXIS_RELATIVE_Y));
- return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
- }
-
- return coords->getAxisValue(axis);
+ const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
+ return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
size_t pointerCount = mPointerProperties.size();
for (size_t i = 0; i < pointerCount; i++) {
- if (mPointerProperties.itemAt(i).id == pointerId) {
+ if (mPointerProperties[i].id == pointerId) {
return i;
}
}
@@ -603,13 +588,14 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) {
void MotionEvent::scale(float globalScaleFactor) {
mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
+ mRawTransform.set(mRawTransform.tx() * globalScaleFactor,
+ mRawTransform.ty() * globalScaleFactor);
mXPrecision *= globalScaleFactor;
mYPrecision *= globalScaleFactor;
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
- mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor,
- globalScaleFactor);
+ mSamplePointerCoords[i].scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
}
}
@@ -619,15 +605,6 @@ void MotionEvent::transform(const std::array<float, 9>& matrix) {
ui::Transform newTransform;
newTransform.set(matrix);
mTransform = newTransform * mTransform;
-
- // We need to update the AXIS_ORIENTATION value here to maintain the old behavior where the
- // orientation angle is not affected by the initial transformation set in the MotionEvent.
- std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(),
- [&newTransform](PointerCoords& c) {
- float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
- c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- transformAngle(newTransform, orientation));
- });
}
void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
@@ -704,21 +681,23 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) {
mYPrecision = parcel->readFloat();
mRawXCursorPosition = parcel->readFloat();
mRawYCursorPosition = parcel->readFloat();
- mDisplayOrientation = parcel->readUint32();
- mDisplayWidth = parcel->readInt32();
- mDisplayHeight = parcel->readInt32();
+
+ result = android::readFromParcel(mRawTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
mDownTime = parcel->readInt64();
mPointerProperties.clear();
- mPointerProperties.setCapacity(pointerCount);
+ mPointerProperties.reserve(pointerCount);
mSampleEventTimes.clear();
mSampleEventTimes.reserve(sampleCount);
mSamplePointerCoords.clear();
- mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
+ mSamplePointerCoords.reserve(sampleCount * pointerCount);
for (size_t i = 0; i < pointerCount; i++) {
- mPointerProperties.push();
- PointerProperties& properties = mPointerProperties.editTop();
+ mPointerProperties.push_back({});
+ PointerProperties& properties = mPointerProperties.back();
properties.id = parcel->readInt32();
properties.toolType = parcel->readInt32();
}
@@ -727,8 +706,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) {
sampleCount--;
mSampleEventTimes.push_back(parcel->readInt64());
for (size_t i = 0; i < pointerCount; i++) {
- mSamplePointerCoords.push();
- status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
+ mSamplePointerCoords.push_back({});
+ status_t status = mSamplePointerCoords.back().readFromParcel(parcel);
if (status) {
return status;
}
@@ -766,18 +745,20 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const {
parcel->writeFloat(mYPrecision);
parcel->writeFloat(mRawXCursorPosition);
parcel->writeFloat(mRawYCursorPosition);
- parcel->writeUint32(mDisplayOrientation);
- parcel->writeInt32(mDisplayWidth);
- parcel->writeInt32(mDisplayHeight);
+
+ result = android::writeToParcel(mRawTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
parcel->writeInt64(mDownTime);
for (size_t i = 0; i < pointerCount; i++) {
- const PointerProperties& properties = mPointerProperties.itemAt(i);
+ const PointerProperties& properties = mPointerProperties[i];
parcel->writeInt32(properties.id);
parcel->writeInt32(properties.toolType);
}
- const PointerCoords* pc = mSamplePointerCoords.array();
+ const PointerCoords* pc = mSamplePointerCoords.data();
for (size_t h = 0; h < sampleCount; h++) {
parcel->writeInt64(mSampleEventTimes[h]);
for (size_t i = 0; i < pointerCount; i++) {
@@ -792,7 +773,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const {
#endif
bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
- if (source & AINPUT_SOURCE_CLASS_POINTER) {
+ if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) {
// Specifically excludes HOVER_MOVE and SCROLL.
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
@@ -830,9 +811,9 @@ std::string MotionEvent::actionToString(int32_t action) {
case AMOTION_EVENT_ACTION_OUTSIDE:
return "OUTSIDE";
case AMOTION_EVENT_ACTION_POINTER_DOWN:
- return "POINTER_DOWN";
+ return StringPrintf("POINTER_DOWN(%" PRId32 ")", MotionEvent::getActionIndex(action));
case AMOTION_EVENT_ACTION_POINTER_UP:
- return "POINTER_UP";
+ return StringPrintf("POINTER_UP(%" PRId32 ")", MotionEvent::getActionIndex(action));
case AMOTION_EVENT_ACTION_HOVER_MOVE:
return "HOVER_MOVE";
case AMOTION_EVENT_ACTION_SCROLL:
@@ -849,19 +830,61 @@ std::string MotionEvent::actionToString(int32_t action) {
return android::base::StringPrintf("%" PRId32, action);
}
+// Apply the given transformation to the point without checking whether the entire transform
+// should be disregarded altogether for the provided source.
+static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
+ const vec2& xy) {
+ return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
+ : transform.transform(xy);
+}
+
+vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
+ const vec2& xy) {
+ if (shouldDisregardTransformation(source)) {
+ return xy;
+ }
+ return calculateTransformedXYUnchecked(source, transform, xy);
+}
+
+float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
+ const ui::Transform& transform,
+ const PointerCoords& coords) {
+ if (shouldDisregardTransformation(source)) {
+ return coords.getAxisValue(axis);
+ }
+
+ if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
+ const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
+ static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+ return xy[axis];
+ }
+
+ if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
+ const vec2 relativeXy =
+ transformWithoutTranslation(transform,
+ {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
+ return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
+ }
+
+ if (axis == AMOTION_EVENT_AXIS_ORIENTATION) {
+ return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ }
+
+ return coords.getAxisValue(axis);
+}
+
// --- FocusEvent ---
-void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) {
+void FocusEvent::initialize(int32_t id, bool hasFocus) {
InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
ADISPLAY_ID_NONE, INVALID_HMAC);
mHasFocus = hasFocus;
- mInTouchMode = inTouchMode;
}
void FocusEvent::initialize(const FocusEvent& from) {
InputEvent::initialize(from);
mHasFocus = from.mHasFocus;
- mInTouchMode = from.mInTouchMode;
}
// --- CaptureEvent ---
@@ -894,6 +917,19 @@ void DragEvent::initialize(const DragEvent& from) {
mY = from.mY;
}
+// --- TouchModeEvent ---
+
+void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) {
+ InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE, INVALID_HMAC);
+ mIsInTouchMode = isInTouchMode;
+}
+
+void TouchModeEvent::initialize(const TouchModeEvent& from) {
+ InputEvent::initialize(from);
+ mIsInTouchMode = from.mIsInTouchMode;
+}
+
// --- PooledInputEventFactory ---
PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
@@ -948,6 +984,15 @@ DragEvent* PooledInputEventFactory::createDragEvent() {
return event;
}
+TouchModeEvent* PooledInputEventFactory::createTouchModeEvent() {
+ if (mTouchModeEventPool.empty()) {
+ return new TouchModeEvent();
+ }
+ TouchModeEvent* event = mTouchModeEventPool.front().release();
+ mTouchModeEventPool.pop();
+ return event;
+}
+
void PooledInputEventFactory::recycle(InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY:
@@ -981,6 +1026,13 @@ void PooledInputEventFactory::recycle(InputEvent* event) {
return;
}
break;
+ case AINPUT_EVENT_TYPE_TOUCH_MODE:
+ if (mTouchModeEventPool.size() < mMaxPoolSize) {
+ mTouchModeEventPool.push(
+ std::unique_ptr<TouchModeEvent>(static_cast<TouchModeEvent*>(event)));
+ return;
+ }
+ break;
}
delete event;
}
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 1aec477081..ac84627b3f 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,7 +21,7 @@
#include <ctype.h>
#include <android-base/stringprintf.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
@@ -208,10 +208,8 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control
const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
int32_t axis, uint32_t source) const {
- size_t numRanges = mMotionRanges.size();
- for (size_t i = 0; i < numRanges; i++) {
- const MotionRange& range = mMotionRanges[i];
- if (range.axis == axis && range.source == source) {
+ for (const MotionRange& range : mMotionRanges) {
+ if (range.axis == axis && isFromSource(range.source, source)) {
return &range;
}
}
@@ -235,7 +233,7 @@ void InputDeviceInfo::addMotionRange(const MotionRange& range) {
void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) {
if (mSensors.find(info.type) != mSensors.end()) {
ALOGW("Sensor type %s already exists, will be replaced by new sensor added.",
- NamedEnum::string(info.type).c_str());
+ ftl::enum_string(info.type).c_str());
}
mSensors.insert_or_assign(info.type, info);
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index ea8b9a7ec8..a065ce25f7 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -30,10 +30,10 @@ static constexpr bool DEBUG_TRANSPORT_ACTIONS = false;
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
#include <cutils/properties.h>
+#include <ftl/enum.h>
#include <log/log.h>
#include <utils/Trace.h>
-#include <ftl/NamedEnum.h>
#include <input/InputTransport.h>
using android::base::StringPrintf;
@@ -116,6 +116,7 @@ bool InputMessage::isValid(size_t actualSize) const {
case Type::FOCUS:
case Type::CAPTURE:
case Type::DRAG:
+ case Type::TOUCH_MODE:
return true;
case Type::TIMELINE: {
const nsecs_t gpuCompletedTime =
@@ -151,6 +152,8 @@ size_t InputMessage::size() const {
return sizeof(Header) + body.drag.size();
case Type::TIMELINE:
return sizeof(Header) + body.timeline.size();
+ case Type::TOUCH_MODE:
+ return sizeof(Header) + body.touchMode.size();
}
return sizeof(Header);
}
@@ -200,6 +203,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const {
case InputMessage::Type::MOTION: {
// int32_t eventId
msg->body.motion.eventId = body.motion.eventId;
+ // uint32_t pointerCount
+ msg->body.motion.pointerCount = body.motion.pointerCount;
// nsecs_t eventTime
msg->body.motion.eventTime = body.motion.eventTime;
// int32_t deviceId
@@ -242,14 +247,14 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const {
msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
// float yCursorPosition
msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
- // uint32_t displayOrientation
- msg->body.motion.displayOrientation = body.motion.displayOrientation;
- // int32_t displayWidth
- msg->body.motion.displayWidth = body.motion.displayWidth;
- // int32_t displayHeight
- msg->body.motion.displayHeight = body.motion.displayHeight;
- // uint32_t pointerCount
- msg->body.motion.pointerCount = body.motion.pointerCount;
+
+ msg->body.motion.dsdxRaw = body.motion.dsdxRaw;
+ msg->body.motion.dtdxRaw = body.motion.dtdxRaw;
+ msg->body.motion.dtdyRaw = body.motion.dtdyRaw;
+ msg->body.motion.dsdyRaw = body.motion.dsdyRaw;
+ msg->body.motion.txRaw = body.motion.txRaw;
+ msg->body.motion.tyRaw = body.motion.tyRaw;
+
//struct Pointer pointers[MAX_POINTERS]
for (size_t i = 0; i < body.motion.pointerCount; i++) {
// PointerProperties properties
@@ -273,7 +278,6 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const {
case InputMessage::Type::FOCUS: {
msg->body.focus.eventId = body.focus.eventId;
msg->body.focus.hasFocus = body.focus.hasFocus;
- msg->body.focus.inTouchMode = body.focus.inTouchMode;
break;
}
case InputMessage::Type::CAPTURE: {
@@ -293,6 +297,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const {
msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
break;
}
+ case InputMessage::Type::TOUCH_MODE: {
+ msg->body.touchMode.eventId = body.touchMode.eventId;
+ msg->body.touchMode.isInTouchMode = body.touchMode.isInTouchMode;
+ }
}
}
@@ -535,8 +543,8 @@ status_t InputPublisher::publishMotionEvent(
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation,
- int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+ float yPrecision, float xCursorPosition, float yCursorPosition,
+ const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
@@ -596,9 +604,12 @@ status_t InputPublisher::publishMotionEvent(
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.xCursorPosition = xCursorPosition;
msg.body.motion.yCursorPosition = yCursorPosition;
- msg.body.motion.displayOrientation = displayOrientation;
- msg.body.motion.displayWidth = displayWidth;
- msg.body.motion.displayHeight = displayHeight;
+ msg.body.motion.dsdxRaw = rawTransform.dsdx();
+ msg.body.motion.dtdxRaw = rawTransform.dtdx();
+ msg.body.motion.dtdyRaw = rawTransform.dtdy();
+ msg.body.motion.dsdyRaw = rawTransform.dsdy();
+ msg.body.motion.txRaw = rawTransform.tx();
+ msg.body.motion.tyRaw = rawTransform.ty();
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
@@ -610,13 +621,10 @@ status_t InputPublisher::publishMotionEvent(
return mChannel->sendMessage(&msg);
}
-status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus,
- bool inTouchMode) {
+status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) {
if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
- mChannel->getName().c_str(), toString(hasFocus),
- toString(inTouchMode));
+ std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)",
+ mChannel->getName().c_str(), toString(hasFocus));
ATRACE_NAME(message.c_str());
}
@@ -625,7 +633,6 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool h
msg.header.seq = seq;
msg.body.focus.eventId = eventId;
msg.body.focus.hasFocus = hasFocus;
- msg.body.focus.inTouchMode = inTouchMode;
return mChannel->sendMessage(&msg);
}
@@ -665,6 +672,22 @@ status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x
return mChannel->sendMessage(&msg);
}
+status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode) {
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)",
+ mChannel->getName().c_str(), toString(isInTouchMode));
+ ATRACE_NAME(message.c_str());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::TOUCH_MODE;
+ msg.header.seq = seq;
+ msg.body.touchMode.eventId = eventId;
+ msg.body.touchMode.isInTouchMode = isInTouchMode;
+ return mChannel->sendMessage(&msg);
+}
+
android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
@@ -691,7 +714,7 @@ android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveC
}
ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
- mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+ mChannel->getName().c_str(), ftl::enum_string(msg.header.type).c_str());
return android::base::Error(UNKNOWN_ERROR);
}
@@ -833,7 +856,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum
case InputMessage::Type::TIMELINE: {
LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
"InputConsumer!",
- NamedEnum::string(mMsg.header.type).c_str());
+ ftl::enum_string(mMsg.header.type).c_str());
break;
}
@@ -866,6 +889,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum
*outEvent = dragEvent;
break;
}
+
+ case InputMessage::Type::TOUCH_MODE: {
+ TouchModeEvent* touchModeEvent = factory->createTouchModeEvent();
+ if (!touchModeEvent) return NO_MEMORY;
+
+ initializeTouchModeEvent(touchModeEvent, &mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = touchModeEvent;
+ break;
+ }
}
}
return OK;
@@ -1333,8 +1366,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg)
}
void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus,
- msg->body.focus.inTouchMode);
+ event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus);
}
void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
@@ -1358,6 +1390,10 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage
ui::Transform transform;
transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
+ ui::Transform displayTransform;
+ displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw,
+ msg->body.motion.txRaw, msg->body.motion.dtdyRaw,
+ msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1});
event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
msg->body.motion.actionButton, msg->body.motion.flags,
@@ -1365,9 +1401,12 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage
msg->body.motion.buttonState, msg->body.motion.classification, transform,
msg->body.motion.xPrecision, msg->body.motion.yPrecision,
msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
- msg->body.motion.displayOrientation, msg->body.motion.displayWidth,
- msg->body.motion.displayHeight, msg->body.motion.downTime,
- msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+ displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,
+ pointerCount, pointerProperties, pointerCoords);
+}
+
+void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
@@ -1412,14 +1451,14 @@ std::string InputConsumer::dump() const {
out = out + "mChannel = " + mChannel->getName() + "\n";
out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
if (mMsgDeferred) {
- out = out + "mMsg : " + NamedEnum::string(mMsg.header.type) + "\n";
+ out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n";
}
out += "Batches:\n";
for (const Batch& batch : mBatches) {
out += " Batch:\n";
for (const InputMessage& msg : batch.samples) {
out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq,
- NamedEnum::string(msg.header.type).c_str());
+ ftl::enum_string(msg.header.type).c_str());
switch (msg.header.type) {
case InputMessage::Type::KEY: {
out += android::base::StringPrintf("action=%s keycode=%" PRId32,
@@ -1446,9 +1485,8 @@ std::string InputConsumer::dump() const {
break;
}
case InputMessage::Type::FOCUS: {
- out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s",
- toString(msg.body.focus.hasFocus),
- toString(msg.body.focus.inTouchMode));
+ out += android::base::StringPrintf("hasFocus=%s",
+ toString(msg.body.focus.hasFocus));
break;
}
case InputMessage::Type::CAPTURE: {
@@ -1476,6 +1514,11 @@ std::string InputConsumer::dump() const {
presentTime);
break;
}
+ case InputMessage::Type::TOUCH_MODE: {
+ out += android::base::StringPrintf("isInTouchMode=%s",
+ toString(msg.body.touchMode.isInTouchMode));
+ break;
+ }
}
out += "\n";
}
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index c365ab070e..7c25cda9ac 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -16,10 +16,8 @@
#define LOG_TAG "KeyLayoutMap"
-#include <stdlib.h>
-
#include <android/keycodes.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
#include <input/InputEventLabels.h>
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
@@ -28,6 +26,10 @@
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
+#include <cstdlib>
+#include <string_view>
+#include <unordered_map>
+
// Enables debug output for the parser.
#define DEBUG_PARSER 0
@@ -39,37 +41,39 @@
namespace android {
+namespace {
-static const char* WHITESPACE = " \t\r";
-
-#define SENSOR_ENTRY(type) NamedEnum::string(type), type
-static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST =
- {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)},
- {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)},
- {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)},
- {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)},
- {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)},
- {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)},
- {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)},
- {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)},
- {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)},
- {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)},
- {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}};
-
-// --- KeyLayoutMap ---
-
-KeyLayoutMap::KeyLayoutMap() {
-}
+constexpr const char* WHITESPACE = " \t\r";
-KeyLayoutMap::~KeyLayoutMap() {
+template <InputDeviceSensorType S>
+constexpr auto sensorPair() {
+ return std::make_pair(ftl::enum_name<S>(), S);
}
+static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_LIST =
+ {sensorPair<InputDeviceSensorType::ACCELEROMETER>(),
+ sensorPair<InputDeviceSensorType::MAGNETIC_FIELD>(),
+ sensorPair<InputDeviceSensorType::ORIENTATION>(),
+ sensorPair<InputDeviceSensorType::GYROSCOPE>(),
+ sensorPair<InputDeviceSensorType::LIGHT>(),
+ sensorPair<InputDeviceSensorType::PRESSURE>(),
+ sensorPair<InputDeviceSensorType::TEMPERATURE>(),
+ sensorPair<InputDeviceSensorType::PROXIMITY>(),
+ sensorPair<InputDeviceSensorType::GRAVITY>(),
+ sensorPair<InputDeviceSensorType::LINEAR_ACCELERATION>(),
+ sensorPair<InputDeviceSensorType::ROTATION_VECTOR>(),
+ sensorPair<InputDeviceSensorType::RELATIVE_HUMIDITY>(),
+ sensorPair<InputDeviceSensorType::AMBIENT_TEMPERATURE>(),
+ sensorPair<InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED>(),
+ sensorPair<InputDeviceSensorType::GAME_ROTATION_VECTOR>(),
+ sensorPair<InputDeviceSensorType::GYROSCOPE_UNCALIBRATED>(),
+ sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()};
+
+} // namespace
+
+KeyLayoutMap::KeyLayoutMap() = default;
+KeyLayoutMap::~KeyLayoutMap() = default;
+
base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
const char* contents) {
Tokenizer* tokenizer;
@@ -160,8 +164,8 @@ base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(
const Sensor& sensor = it->second;
#if DEBUG_MAPPING
- ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode,
- NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex);
+ ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode,
+ ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex);
#endif
return std::make_pair(sensor.sensorType, sensor.sensorDataIndex);
}
@@ -513,7 +517,7 @@ status_t KeyLayoutMap::Parser::parseLed() {
}
static std::optional<InputDeviceSensorType> getSensorType(const char* token) {
- auto it = SENSOR_LIST.find(std::string(token));
+ auto it = SENSOR_LIST.find(token);
if (it == SENSOR_LIST.end()) {
return std::nullopt;
}
@@ -581,8 +585,8 @@ status_t KeyLayoutMap::Parser::parseSensor() {
int32_t sensorDataIndex = indexOpt.value();
#if DEBUG_PARSER
- ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code,
- NamedEnum::string(sensorType).c_str(), sensorDataIndex);
+ ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
+ ftl::enum_string(sensorType).c_str(), sensorDataIndex);
#endif
Sensor sensor;
@@ -591,4 +595,5 @@ status_t KeyLayoutMap::Parser::parseSensor() {
map.emplace(code, sensor);
return NO_ERROR;
}
-};
+
+} // namespace android
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index a44f0b7fe0..a6465eec24 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -18,10 +18,10 @@
//#define LOG_NDEBUG 0
// Log debug messages about velocity tracking.
-#define DEBUG_VELOCITY 0
+static constexpr bool DEBUG_VELOCITY = false;
// Log debug messages about the progress of the algorithm itself.
-#define DEBUG_STRATEGY 0
+static constexpr bool DEBUG_STRATEGY = false;
#include <array>
#include <inttypes.h>
@@ -30,7 +30,6 @@
#include <optional>
#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
#include <input/VelocityTracker.h>
#include <utils/BitSet.h>
#include <utils/Timers.h>
@@ -64,7 +63,6 @@ static float vectorNorm(const float* a, uint32_t m) {
return sqrtf(r);
}
-#if DEBUG_STRATEGY || DEBUG_VELOCITY
static std::string vectorToString(const float* a, uint32_t m) {
std::string str;
str += "[";
@@ -77,9 +75,11 @@ static std::string vectorToString(const float* a, uint32_t m) {
str += " ]";
return str;
}
-#endif
-#if DEBUG_STRATEGY
+static std::string vectorToString(const std::vector<float>& v) {
+ return vectorToString(v.data(), v.size());
+}
+
static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
std::string str;
str = "[";
@@ -99,7 +99,6 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r
str += " ]";
return str;
}
-#endif
// --- VelocityTracker ---
@@ -133,12 +132,18 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
VelocityTracker::Strategy strategy) {
switch (strategy) {
case VelocityTracker::Strategy::IMPULSE:
+ if (DEBUG_STRATEGY) {
+ ALOGI("Initializing impulse strategy");
+ }
return std::make_unique<ImpulseVelocityTrackerStrategy>();
case VelocityTracker::Strategy::LSQ1:
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
case VelocityTracker::Strategy::LSQ2:
+ if (DEBUG_STRATEGY) {
+ ALOGI("Initializing lsq2 strategy");
+ }
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
case VelocityTracker::Strategy::LSQ3:
@@ -204,10 +209,10 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
if ((mCurrentPointerIdBits.value & idBits.value)
&& eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
- (eventTime - mLastEventTime) * 0.000001f);
-#endif
+ if (DEBUG_VELOCITY) {
+ ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
+ (eventTime - mLastEventTime) * 0.000001f);
+ }
// We have not received any movements for too long. Assume that all pointers
// have stopped.
mStrategy->clear();
@@ -221,24 +226,24 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
mStrategy->addMovement(eventTime, idBits, positions);
-#if DEBUG_VELOCITY
- 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();
- uint32_t index = idBits.getIndexOfBit(id);
- iterBits.clearBit(id);
- Estimator estimator;
- getEstimator(id, &estimator);
- ALOGD(" %d: position (%0.3f, %0.3f), "
- "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).c_str(),
- vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
- estimator.confidence);
+ if (DEBUG_VELOCITY) {
+ 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();
+ uint32_t index = idBits.getIndexOfBit(id);
+ iterBits.clearBit(id);
+ Estimator estimator;
+ getEstimator(id, &estimator);
+ ALOGD(" %d: position (%0.3f, %0.3f), "
+ "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).c_str(),
+ vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
+ estimator.confidence);
+ }
}
-#endif
}
void VelocityTracker::addMovement(const MotionEvent* event) {
@@ -419,11 +424,10 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(
static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
const size_t m = x.size();
-#if DEBUG_STRATEGY
- ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
- vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
- vectorToString(w, m).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
+ vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str());
+ }
LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
// Expand the X vector to a matrix A, pre-multiplied by the weights.
@@ -434,9 +438,9 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo
a[i][h] = a[i - 1][h] * x[h];
}
}
-#if DEBUG_STRATEGY
- ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
+ }
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
float q[n][m]; // orthonormal basis, column-major order
@@ -455,9 +459,9 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo
float norm = vectorNorm(&q[j][0], m);
if (norm < 0.000001f) {
// vectors are linearly dependent or zero so no solution
-#if DEBUG_STRATEGY
- ALOGD(" - no solution, norm=%f", norm);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - no solution, norm=%f", norm);
+ }
return false;
}
@@ -469,22 +473,22 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo
r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
}
}
-#if DEBUG_STRATEGY
- 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());
+ if (DEBUG_STRATEGY) {
+ 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];
- for (uint32_t h = 0; h < m; h++) {
- for (uint32_t i = 0; i < n; i++) {
- qr[i][h] = 0;
- for (uint32_t j = 0; j < n; j++) {
- qr[i][h] += q[j][h] * r[j][i];
+ // calculate QR, if we factored A correctly then QR should equal A
+ float qr[n][m];
+ for (uint32_t h = 0; h < m; h++) {
+ for (uint32_t i = 0; i < n; i++) {
+ qr[i][h] = 0;
+ for (uint32_t j = 0; j < n; j++) {
+ qr[i][h] += q[j][h] * r[j][i];
+ }
}
}
+ ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
}
- 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.
// We just work from bottom-right to top-left calculating B's coefficients.
@@ -500,9 +504,9 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo
}
outB[i] /= r[i][i];
}
-#if DEBUG_STRATEGY
- ALOGD(" - b=%s", vectorToString(outB, n).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - b=%s", vectorToString(outB, n).c_str());
+ }
// Calculate the coefficient of determination as 1 - (SSerr / SStot) where
// SSerr is the residual sum of squares (variance of the error),
@@ -528,11 +532,11 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo
sstot += w[h] * w[h] * var * var;
}
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
-#if DEBUG_STRATEGY
- ALOGD(" - sserr=%f", sserr);
- ALOGD(" - sstot=%f", sstot);
- ALOGD(" - det=%f", *outDet);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - sserr=%f", sserr);
+ ALOGD(" - sstot=%f", sstot);
+ ALOGD(" - det=%f", *outDet);
+ }
return true;
}
@@ -655,13 +659,11 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = degree;
outEstimator->confidence = xdet * ydet;
-#if DEBUG_STRATEGY
- ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
- int(outEstimator->degree),
- vectorToString(outEstimator->xCoeff, n).c_str(),
- vectorToString(outEstimator->yCoeff, n).c_str(),
- outEstimator->confidence);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
+ int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
+ vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
+ }
return true;
}
}
@@ -1169,9 +1171,9 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id,
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = 2; // similar results to 2nd degree fit
outEstimator->confidence = 1;
-#if DEBUG_STRATEGY
- ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
+ }
return true;
}
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 474a1e410d..829bbdd0b7 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -25,13 +25,6 @@ interface IInputConstants
// android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS.
const int UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
- // Compatibility changes.
- /**
- * TODO(b/157929241): remove this before closing the bug. This is needed temporarily
- * to identify apps that are using this flag.
- */
- const long BLOCK_FLAG_SLIPPERY = 157929241;
-
// Indicate invalid battery capacity
const int INVALID_BATTERY_CAPACITY = -1;
@@ -53,4 +46,53 @@ interface IInputConstants
* set of flags, including in input/Input.h and in android/input.h.
*/
const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
+
+ @Backing(type="int")
+ enum InputFeature {
+ /**
+ * Does not construct an input channel for this window. The channel will therefore
+ * be incapable of receiving input.
+ */
+ NO_INPUT_CHANNEL = 0x00000002,
+
+ /**
+ * When this window has focus, does not call user activity for all input events so
+ * the application will have to do it itself. Should only be used by
+ * the keyguard and phone app.
+ *
+ * Should only be used by the keyguard and phone app.
+ */
+ DISABLE_USER_ACTIVITY = 0x00000004,
+
+ /**
+ * Internal flag used to indicate that input should be dropped on this window.
+ */
+ DROP_INPUT = 0x00000008,
+
+ /**
+ * Internal flag used to indicate that input should be dropped on this window if this window
+ * is obscured.
+ */
+ DROP_INPUT_IF_OBSCURED = 0x00000010,
+
+ /**
+ * An input spy window. This window will receive all pointer events within its touchable
+ * area, but will will not stop events from being sent to other windows below it in z-order.
+ * An input event will be dispatched to all spy windows above the top non-spy window at the
+ * event's coordinates.
+ */
+ SPY = 0x00000020,
+
+ /**
+ * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue
+ * to receive events from a stylus device within its touchable region. All other pointer
+ * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it.
+ *
+ * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is
+ * not set.
+ *
+ * The window must be a trusted overlay to use this input feature.
+ */
+ INTERCEPTS_STYLUS = 0x00000040,
+ }
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index b1ef7534e4..a92016ba3b 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -226,39 +226,23 @@ protected:
static constexpr float Y_SCALE = 3.0;
static constexpr float X_OFFSET = 1;
static constexpr float Y_OFFSET = 1.1;
-
- static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
+ static constexpr float RAW_X_SCALE = 4.0;
+ static constexpr float RAW_Y_SCALE = -5.0;
+ static constexpr float RAW_X_OFFSET = 12;
+ static constexpr float RAW_Y_OFFSET = -41.1;
int32_t mId;
ui::Transform mTransform;
-
- void SetUp() override;
- void TearDown() override;
+ ui::Transform mRawTransform;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-const std::optional<bool> MotionEventTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
- !base::GetProperty("persist.debug.per_window_input_rotation", "").empty()
- ? std::optional(base::GetBoolProperty("persist.debug.per_window_input_rotation", false))
- : std::nullopt;
-
-void MotionEventTest::SetUp() {
- // Ensure per_window_input_rotation is enabled.
- base::SetProperty("persist.debug.per_window_input_rotation", "true");
-}
-
-void MotionEventTest::TearDown() {
- const auto val = INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE.has_value()
- ? (*INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE ? "true" : "false")
- : "";
- base::SetProperty("persist.debug.per_window_input_rotation", val);
-}
-
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
mId = InputEvent::nextId();
mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
+ mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1});
PointerProperties pointerProperties[2];
pointerProperties[0].clear();
@@ -294,9 +278,8 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
- pointerCoords);
+ mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
+ pointerProperties, pointerCoords);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -373,39 +356,37 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
- ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(211, event->getRawPointerCoords(0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(221, event->getRawPointerCoords(1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
-
- ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
- ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
- ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
- ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
- ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
- ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
-
- ASSERT_EQ(10, event->getHistoricalRawX(0, 0));
- ASSERT_EQ(20, event->getHistoricalRawX(1, 0));
- ASSERT_EQ(110, event->getHistoricalRawX(0, 1));
- ASSERT_EQ(120, event->getHistoricalRawX(1, 1));
- ASSERT_EQ(210, event->getRawX(0));
- ASSERT_EQ(220, event->getRawX(1));
-
- ASSERT_EQ(11, event->getHistoricalRawY(0, 0));
- ASSERT_EQ(21, event->getHistoricalRawY(1, 0));
- ASSERT_EQ(111, event->getHistoricalRawY(0, 1));
- ASSERT_EQ(121, event->getHistoricalRawY(1, 1));
- ASSERT_EQ(211, event->getRawY(0));
- ASSERT_EQ(221, event->getRawY(1));
+ ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+
+ ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+
+ ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0));
+ ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0));
+ ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1));
+ ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1));
+ ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0));
+ ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1));
+
+ ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0));
+ ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1));
ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
@@ -463,12 +444,19 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
ASSERT_EQ(217, event->getToolMinor(0));
ASSERT_EQ(227, event->getToolMinor(1));
- ASSERT_EQ(18, event->getHistoricalOrientation(0, 0));
- ASSERT_EQ(28, event->getHistoricalOrientation(1, 0));
- ASSERT_EQ(118, event->getHistoricalOrientation(0, 1));
- ASSERT_EQ(128, event->getHistoricalOrientation(1, 1));
- ASSERT_EQ(218, event->getOrientation(0));
- ASSERT_EQ(228, event->getOrientation(1));
+ // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is "up",
+ // and the positive y direction is "down".
+ auto toScaledOrientation = [](float angle) {
+ const float x = sinf(angle) * X_SCALE;
+ const float y = -cosf(angle) * Y_SCALE;
+ return atan2f(x, -y);
+ };
+ ASSERT_EQ(toScaledOrientation(18), event->getHistoricalOrientation(0, 0));
+ ASSERT_EQ(toScaledOrientation(28), event->getHistoricalOrientation(1, 0));
+ ASSERT_EQ(toScaledOrientation(118), event->getHistoricalOrientation(0, 1));
+ ASSERT_EQ(toScaledOrientation(128), event->getHistoricalOrientation(1, 1));
+ ASSERT_EQ(toScaledOrientation(218), event->getOrientation(0));
+ ASSERT_EQ(toScaledOrientation(228), event->getOrientation(1));
}
TEST_F(MotionEventTest, Properties) {
@@ -537,14 +525,15 @@ TEST_F(MotionEventTest, OffsetLocation) {
TEST_F(MotionEventTest, Scale) {
MotionEvent event;
initializeEventWithHistory(&event);
+ const float unscaledOrientation = event.getOrientation(0);
event.scale(2.0f);
ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
- ASSERT_EQ(210 * 2, event.getRawX(0));
- ASSERT_EQ(211 * 2, event.getRawY(0));
+ ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0));
+ ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0));
ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
@@ -553,7 +542,7 @@ TEST_F(MotionEventTest, Scale) {
ASSERT_EQ(215 * 2, event.getTouchMinor(0));
ASSERT_EQ(216 * 2, event.getToolMajor(0));
ASSERT_EQ(217 * 2, event.getToolMinor(0));
- ASSERT_EQ(218, event.getOrientation(0));
+ ASSERT_EQ(unscaledOrientation, event.getOrientation(0));
}
TEST_F(MotionEventTest, Parcel) {
@@ -592,10 +581,10 @@ TEST_F(MotionEventTest, Transform) {
// The geometrical representation is irrelevant to the test, it's just easy to generate
// and check rotation. We set the orientation to the same angle.
// Coordinate system: down is increasing Y, right is increasing X.
- const float PI_180 = float(M_PI / 180);
- const float RADIUS = 10;
- const float ARC = 36;
- const float ROTATION = ARC * 2;
+ static constexpr float PI_180 = float(M_PI / 180);
+ static constexpr float RADIUS = 10;
+ static constexpr float ARC = 36;
+ static constexpr float ROTATION = ARC * 2;
const size_t pointerCount = 11;
PointerProperties pointerProperties[pointerCount];
@@ -616,9 +605,8 @@ TEST_F(MotionEventTest, Transform) {
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
- pointerCoords);
+ identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
+ pointerProperties, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -659,9 +647,8 @@ TEST_F(MotionEventTest, Transform) {
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
-MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
- const ui::Transform& transform,
- uint32_t displayOrientation = ui::Transform::ROT_0) {
+MotionEvent createMotionEvent(int32_t source, uint32_t action, float x, float y, float dx, float dy,
+ const ui::Transform& transform, const ui::Transform& rawTransform) {
std::vector<PointerProperties> pointerProperties;
pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
std::vector<PointerCoords> pointerCoords;
@@ -672,24 +659,30 @@ MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
MotionEvent event;
- event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
- /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+ event.initialize(InputEvent::nextId(), /* deviceId */ 1, source,
+ /* displayId */ 0, INVALID_HMAC, action,
/* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
/* buttonState */ 0, MotionClassification::NONE, transform,
/* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, displayOrientation,
- /* displayWidth */ 400,
- /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
- pointerProperties.data(), pointerCoords.data());
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, rawTransform, eventTime, eventTime,
+ pointerCoords.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
+MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
+ const ui::Transform& transform,
+ const ui::Transform& rawTransform) {
+ return createMotionEvent(AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_DOWN, x, y, dx, dy,
+ transform, rawTransform);
+}
+
TEST_F(MotionEventTest, ApplyTransform) {
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform identity;
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+ ui::Transform rawTransform(ui::Transform::ROT_90, 800, 400);
+ MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform);
ASSERT_EQ(700, event.getRawX(0));
ASSERT_EQ(60, event.getRawY(0));
ASSERT_NE(event.getRawX(0), event.getX(0));
@@ -698,10 +691,10 @@ TEST_F(MotionEventTest, ApplyTransform) {
ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity);
- const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
- xform[0][1], xform[1][1], xform[2][1],
- xform[0][2], xform[1][2], xform[2][2]};
+ MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity, identity);
+ const std::array<float, 9> rowMajor{transform[0][0], transform[1][0], transform[2][0],
+ transform[0][1], transform[1][1], transform[2][1],
+ transform[0][2], transform[1][2], transform[2][2]};
changedEvent.applyTransform(rowMajor);
// transformContent effectively rotates the raw coordinates, so those should now include
@@ -721,16 +714,39 @@ TEST_F(MotionEventTest, ApplyTransform) {
changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001);
}
+TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
+ constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
+ AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_JOYSTICK,
+ AMOTION_EVENT_ACTION_MOVE)};
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+
+ for (const auto& [source, action] : kNonTransformedSources) {
+ const MotionEvent event =
+ createMotionEvent(source, action, 60, 100, 0, 0, transform, transform);
+
+ // These events should not be transformed in any way.
+ ASSERT_EQ(60, event.getX(0));
+ ASSERT_EQ(100, event.getY(0));
+ ASSERT_EQ(event.getRawX(0), event.getX(0));
+ ASSERT_EQ(event.getRawY(0), event.getY(0));
+ }
+}
+
TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
- constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL,
- AINPUT_SOURCE_MOUSE_RELATIVE,
- AINPUT_SOURCE_JOYSTICK};
- for (uint32_t source : NON_POINTER_SOURCES) {
- // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
- event.setSource(source);
+ constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
+ AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+ AMOTION_EVENT_ACTION_MOVE)};
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+
+ for (const auto& [source, action] : kNonPointerSources) {
+ const MotionEvent event =
+ createMotionEvent(source, action, 60, 100, 42, 96, transform, transform);
// Since this event comes from a non-pointer source, it should include rotation but not
// translation/offset.
@@ -741,72 +757,34 @@ TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
}
}
-TEST_F(MotionEventTest, RawCompatTransform) {
- {
- // Make sure raw is raw regardless of transform translation.
- ui::Transform xform;
- xform.set(20, 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform);
- ASSERT_EQ(60, event.getRawX(0));
- ASSERT_EQ(100, event.getRawY(0));
- ASSERT_NE(event.getRawX(0), event.getX(0));
- ASSERT_NE(event.getRawY(0), event.getY(0));
- // Relative values should not be modified.
- ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+TEST_F(MotionEventTest, AxesAreCorrectlyTransformed) {
+ const ui::Transform identity;
+ ui::Transform transform;
+ transform.set({1.1, -2.2, 3.3, -4.4, 5.5, -6.6, 0, 0, 1});
+ ui::Transform rawTransform;
+ rawTransform.set({-6.6, 5.5, -4.4, 3.3, -2.2, 1.1, 0, 0, 1});
+ auto transformWithoutTranslation = [](const ui::Transform& t, float x, float y) {
+ auto newPoint = t.transform(x, y);
+ auto newOrigin = t.transform(0, 0);
+ return newPoint - newOrigin;
+ };
- // Next check that getRaw contains rotation (for compatibility) but otherwise is still
- // "Screen-space". The following tests check all 3 rotations.
- {
- // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
- ASSERT_EQ(700, event.getRawX(0));
- ASSERT_EQ(60, event.getRawY(0));
- ASSERT_NE(event.getRawX(0), event.getX(0));
- ASSERT_NE(event.getRawY(0), event.getY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ const MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform);
- {
- // Same as above, but check rotate-180.
- ui::Transform xform(ui::Transform::ROT_180, 400, 800);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_180);
- ASSERT_EQ(340, event.getRawX(0));
- ASSERT_EQ(700, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // The x and y axes should have the window transform applied.
+ const auto newPoint = transform.transform(60, 100);
+ ASSERT_EQ(newPoint.x, event.getX(0));
+ ASSERT_EQ(newPoint.y, event.getY(0));
- {
- // Same as above, but check rotate-270.
- ui::Transform xform(ui::Transform::ROT_270, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_270);
- ASSERT_EQ(100, event.getRawX(0));
- ASSERT_EQ(340, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // The raw values should have the display transform applied.
+ const auto raw = rawTransform.transform(60, 100);
+ ASSERT_EQ(raw.x, event.getRawX(0));
+ ASSERT_EQ(raw.y, event.getRawY(0));
- {
- // Finally, check that raw isn't effected by transform
- ui::Transform xform(ui::Transform::ROT_270, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
- ASSERT_EQ(700, event.getRawX(0));
- ASSERT_EQ(60, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // Relative values should have the window transform applied without any translation.
+ const auto rel = transformWithoutTranslation(transform, 42, 96);
+ ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+ ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
}
TEST_F(MotionEventTest, Initialize_SetsClassification) {
@@ -832,8 +810,7 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) {
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
}
@@ -854,9 +831,9 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) {
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
- pointerCount, pointerProperties, pointerCoords);
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identityTransform,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
ASSERT_EQ(540, event.getRawYCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 5d1f2c3bfc..05bc0bcbe8 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -56,6 +56,7 @@ protected:
void PublishAndConsumeFocusEvent();
void PublishAndConsumeCaptureEvent();
void PublishAndConsumeDragEvent();
+ void PublishAndConsumeTouchModeEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -159,13 +160,14 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
constexpr float yScale = 3;
constexpr float xOffset = -10;
constexpr float yOffset = -20;
+ constexpr float rawXScale = 4;
+ constexpr float rawYScale = -5;
+ constexpr float rawXOffset = -11;
+ constexpr float rawYOffset = 42;
constexpr float xPrecision = 0.25;
constexpr float yPrecision = 0.5;
constexpr float xCursorPosition = 1.3;
constexpr float yCursorPosition = 50.6;
- constexpr uint32_t displayOrientation = ui::Transform::ROT_0;
- constexpr int32_t displayWidth = 1000;
- constexpr int32_t displayHeight = 2000;
constexpr nsecs_t downTime = 3;
constexpr size_t pointerCount = 3;
constexpr nsecs_t eventTime = 4;
@@ -191,12 +193,14 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
ui::Transform transform;
transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
+ ui::Transform rawTransform;
+ rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1});
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
classification, transform, xPrecision, yPrecision,
- xCursorPosition, yCursorPosition, displayOrientation,
- displayWidth, displayHeight, downTime, eventTime,
- pointerCount, pointerProperties, pointerCoords);
+ xCursorPosition, yCursorPosition, rawTransform,
+ downTime, eventTime, pointerCount, pointerProperties,
+ pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -233,9 +237,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
- EXPECT_EQ(displayOrientation, motionEvent->getDisplayOrientation());
- EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x);
- EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y);
+ EXPECT_EQ(rawTransform, motionEvent->getRawTransform());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -246,28 +248,24 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- motionEvent->getRawX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- motionEvent->getRawY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
- motionEvent->getX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
- motionEvent->getY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- motionEvent->getPressure(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- motionEvent->getSize(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- motionEvent->getTouchMajor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- motionEvent->getTouchMinor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- motionEvent->getToolMajor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- motionEvent->getToolMinor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
- motionEvent->getOrientation(i));
+ const auto& pc = pointerCoords[i];
+ EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i));
+ EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i));
+ EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i));
+ EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i));
+
+ // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is
+ // "up", and the positive y direction is "down".
+ const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ const float x = sinf(unscaledOrientation) * xScale;
+ const float y = -cosf(unscaledOrientation) * yScale;
+ EXPECT_EQ(atan2f(x, -y), motionEvent->getOrientation(i));
}
status = mConsumer->sendFinishedSignal(seq, false);
@@ -292,10 +290,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
constexpr uint32_t seq = 15;
int32_t eventId = InputEvent::nextId();
constexpr bool hasFocus = true;
- constexpr bool inTouchMode = true;
const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
- status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
+ status = mPublisher->publishFocusEvent(seq, eventId, hasFocus);
ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK";
uint32_t consumeSeq;
@@ -311,7 +308,6 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
EXPECT_EQ(seq, consumeSeq);
EXPECT_EQ(eventId, focusEvent->getId());
EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
- EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
@@ -413,6 +409,46 @@ void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() {
<< "finished signal's consume time should be greater than publish time";
}
+void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool touchModeEnabled = true;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled);
+ ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType())
+ << "consumer should have returned a touch mode event";
+
+ const TouchModeEvent& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(eventId, touchModeEvent.getId());
+ EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
+ << "finished signal's consume time should be greater than publish time";
+}
+
TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
const int32_t inputEventId = 20;
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
@@ -449,6 +485,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
}
+TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
status_t status;
const size_t pointerCount = 1;
@@ -460,12 +500,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer
}
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -477,12 +517,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha
PointerCoords pointerCoords[pointerCount];
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -499,12 +539,12 @@ TEST_F(InputPublisherAndConsumerTest,
}
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -520,6 +560,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
}
} // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 59fed1fb41..b6a94764e5 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -49,7 +49,7 @@ void TestInputMessageAlignment() {
CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
- CHECK_OFFSET(InputMessage::Body::Motion, empty1, 4);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 4);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -74,16 +74,17 @@ void TestInputMessageAlignment() {
CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
- CHECK_OFFSET(InputMessage::Body::Motion, displayOrientation, 136);
- CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 140);
- CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 144);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 148);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdxRaw, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdxRaw, 140);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdyRaw, 144);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdyRaw, 148);
+ CHECK_OFFSET(InputMessage::Body::Motion, txRaw, 152);
+ CHECK_OFFSET(InputMessage::Body::Motion, tyRaw, 156);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 160);
CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
- CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5);
- CHECK_OFFSET(InputMessage::Body::Focus, empty, 6);
+ CHECK_OFFSET(InputMessage::Body::Focus, empty, 5);
CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
@@ -102,6 +103,10 @@ void TestInputMessageAlignment() {
CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
+
+ CHECK_OFFSET(InputMessage::Body::TouchMode, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::TouchMode, isInTouchMode, 4);
+ CHECK_OFFSET(InputMessage::Body::TouchMode, empty, 5);
}
void TestHeaderSize() {
@@ -123,6 +128,7 @@ void TestBodySize() {
static_assert(sizeof(InputMessage::Body::Focus) == 8);
static_assert(sizeof(InputMessage::Body::Capture) == 8);
static_assert(sizeof(InputMessage::Body::Drag) == 16);
+ static_assert(sizeof(InputMessage::Body::TouchMode) == 8);
// Timeline
static_assert(GraphicsTimeline::SIZE == 2);
static_assert(sizeof(InputMessage::Body::Timeline) == 24);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 13e2b02ca4..a87b1873f0 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -184,8 +184,7 @@ static std::vector<MotionEvent> createMotionEventStream(
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
events.emplace_back(event);
@@ -344,7 +343,7 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) {
{ 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
};
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
- 872.794617);
+ 764.345703);
computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
951.698181);
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index b29c9a4877..f2b59ea9ab 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -43,12 +43,12 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) {
ui::Transform transform;
transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
+ ui::Transform identity;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 100 /*downTime*/,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identity, 100 /*downTime*/,
200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
return event;
}
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 2dd6c4fcaa..d90ee57322 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -77,6 +77,7 @@ namespace android {
struct FrameCallback {
AChoreographer_frameCallback callback;
AChoreographer_frameCallback64 callback64;
+ AChoreographer_extendedFrameCallback extendedCallback;
void* data;
nsecs_t dueTime;
@@ -95,6 +96,20 @@ struct RefreshRateCallback {
class Choreographer;
+/**
+ * Implementation of AChoreographerFrameCallbackData.
+ */
+struct ChoreographerFrameCallbackDataImpl {
+ int64_t frameTimeNanos{0};
+
+ std::array<VsyncEventData::FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength>
+ frameTimelines;
+
+ size_t preferredFrameTimelineIndex;
+
+ const Choreographer* choreographer;
+};
+
struct {
std::mutex lock;
std::vector<Choreographer*> ptrs GUARDED_BY(lock);
@@ -107,7 +122,9 @@ class Choreographer : public DisplayEventDispatcher, public MessageHandler {
public:
explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_extendedFrameCallback extendedCallback, void* data,
+ nsecs_t delay);
void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
EXCLUDES(gChoreographers.lock);
void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
@@ -127,9 +144,8 @@ public:
static Choreographer* getForThread();
virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
- int64_t getVsyncId() const;
- int64_t getFrameDeadline() const;
int64_t getFrameInterval() const;
+ bool inCallback() const;
private:
Choreographer(const Choreographer&) = delete;
@@ -145,6 +161,8 @@ private:
void scheduleCallbacks();
+ ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+
std::mutex mLock;
// Protected by mLock
std::priority_queue<FrameCallback> mFrameCallbacks;
@@ -152,6 +170,7 @@ private:
nsecs_t mLatestVsyncPeriod = -1;
VsyncEventData mLastVsyncEventData;
+ bool mInCallback = false;
const sp<Looper> mLooper;
const std::thread::id mThreadId;
@@ -192,6 +211,7 @@ Choreographer::~Choreographer() {
// Only poke DisplayManagerGlobal to unregister if we previously registered
// callbacks.
if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+ gChoreographers.registeredToDisplayManager = false;
JNIEnv* env = getJniEnv();
if (env == nullptr) {
ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
@@ -210,10 +230,12 @@ Choreographer::~Choreographer() {
}
}
-void Choreographer::postFrameCallbackDelayed(
- AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
+void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_extendedFrameCallback extendedCallback,
+ void* data, nsecs_t delay) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, data, now + delay};
+ FrameCallback callback{cb, cb64, extendedCallback, data, now + delay};
{
std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
@@ -305,8 +327,9 @@ void Choreographer::scheduleLatestConfigRequest() {
// Fortunately, these events are small so sending packets across the
// socket should be atomic across processes.
DisplayEventReceiver::Event event;
- event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
- PhysicalDisplayId(0), systemTime()};
+ event.header =
+ DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+ PhysicalDisplayId::fromPort(0), systemTime()};
injectEvent(event);
}
}
@@ -368,7 +391,15 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t
}
mLastVsyncEventData = vsyncEventData;
for (const auto& cb : callbacks) {
- if (cb.callback64 != nullptr) {
+ if (cb.extendedCallback != nullptr) {
+ const ChoreographerFrameCallbackDataImpl frameCallbackData =
+ createFrameCallbackData(timestamp);
+ mInCallback = true;
+ cb.extendedCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
+ &frameCallbackData),
+ cb.data);
+ mInCallback = false;
+ } else if (cb.callback64 != nullptr) {
cb.callback64(timestamp, cb.data);
} else if (cb.callback != nullptr) {
cb.callback(timestamp, cb.data);
@@ -377,8 +408,8 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t
}
void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.",
- this, to_string(displayId).c_str(), toString(connected));
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
+ to_string(displayId).c_str(), toString(connected));
}
void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
@@ -397,28 +428,31 @@ void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
void Choreographer::handleMessage(const Message& message) {
switch (message.what) {
- case MSG_SCHEDULE_CALLBACKS:
- scheduleCallbacks();
- break;
- case MSG_SCHEDULE_VSYNC:
- scheduleVsync();
- break;
- case MSG_HANDLE_REFRESH_RATE_UPDATES:
- handleRefreshRateUpdates();
- break;
+ case MSG_SCHEDULE_CALLBACKS:
+ scheduleCallbacks();
+ break;
+ case MSG_SCHEDULE_VSYNC:
+ scheduleVsync();
+ break;
+ case MSG_HANDLE_REFRESH_RATE_UPDATES:
+ handleRefreshRateUpdates();
+ break;
}
}
-int64_t Choreographer::getVsyncId() const {
- return mLastVsyncEventData.id;
+int64_t Choreographer::getFrameInterval() const {
+ return mLastVsyncEventData.frameInterval;
}
-int64_t Choreographer::getFrameDeadline() const {
- return mLastVsyncEventData.deadlineTimestamp;
+bool Choreographer::inCallback() const {
+ return mInCallback;
}
-int64_t Choreographer::getFrameInterval() const {
- return mLastVsyncEventData.frameInterval;
+ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
+ return {.frameTimeNanos = timestamp,
+ .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex,
+ .frameTimelines = mLastVsyncEventData.frameTimelines,
+ .choreographer = this};
}
} // namespace android
@@ -433,6 +467,12 @@ static inline const Choreographer* AChoreographer_to_Choreographer(
return reinterpret_cast<const Choreographer*>(choreographer);
}
+static inline const ChoreographerFrameCallbackDataImpl*
+AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(
+ const AChoreographerFrameCallbackData* data) {
+ return reinterpret_cast<const ChoreographerFrameCallbackDataImpl*>(data);
+}
+
// Glue for private C api
namespace android {
void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
@@ -485,6 +525,11 @@ void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographe
void* data, uint32_t delayMillis) {
return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
}
+void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data) {
+ return AChoreographer_postExtendedFrameCallback(choreographer, callback, data);
+}
void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data) {
@@ -495,13 +540,29 @@ void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreogra
void* data) {
return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
}
-
-int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) {
- return AChoreographer_to_Choreographer(choreographer)->getVsyncId();
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getFrameTimeNanos(data);
}
-
-int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) {
- return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline();
+size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getFrameTimelinesLength(data);
+}
+size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(data);
+}
+AVsyncId AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineVsyncId(data, index);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(data, index);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(data, index);
}
int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) {
@@ -521,24 +582,32 @@ AChoreographer* AChoreographer_getInstance() {
}
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, 0);
+ AChoreographer_frameCallback callback, void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data, long delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, ms2ns(delayMillis));
+ AChoreographer_frameCallback callback, void* data,
+ long delayMillis) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
+}
+void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
}
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, 0);
+ AChoreographer_frameCallback64 callback, void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, ms2ns(delayMillis));
+ AChoreographer_frameCallback64 callback, void* data,
+ uint32_t delayMillis) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis));
}
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
@@ -551,6 +620,58 @@ void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
}
+int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->frameTimeNanos;
+}
+size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->frameTimelines.size();
+}
+size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->preferredFrameTimelineIndex;
+}
+AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].id;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].expectedPresentTime;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].deadlineTimestamp;
+}
+
AChoreographer* AChoreographer_create() {
Choreographer* choreographer = new Choreographer(nullptr);
status_t result = choreographer->initialize();
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 6288194714..76b85d6002 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -50,11 +50,6 @@ struct DisplayConfigImpl {
int32_t height{0};
/**
- * The display density.
- */
- float density{0};
-
- /**
* The refresh rate of the display configuration, in frames per second.
*/
float fps{0.0};
@@ -168,8 +163,8 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
const ui::DisplayMode& mode = modes[j];
modesPerDisplay[i].emplace_back(
DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(),
- mode.resolution.getHeight(), staticInfo.density,
- mode.refreshRate, mode.sfVsyncOffset, mode.appVsyncOffset});
+ mode.resolution.getHeight(), mode.refreshRate,
+ mode.sfVsyncOffset, mode.appVsyncOffset});
}
}
@@ -283,12 +278,6 @@ int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
return NAME_NOT_FOUND;
}
-float ADisplayConfig_getDensity(ADisplayConfig* config) {
- CHECK_NOT_NULL(config);
-
- return reinterpret_cast<DisplayConfigImpl*>(config)->density;
-}
-
int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
CHECK_NOT_NULL(config);
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 7d25ce8253..d650c26605 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -29,19 +29,6 @@ void AChoreographer_initJVM(JNIEnv* env);
// for consumption by callbacks.
void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
-// Returns the vsync id of the last frame callback. Client are expected to call
-// this function from their frame callback function to get the vsyncId and pass
-// it together with a buffer or transaction to the Surface Composer. Calling
-// this function from anywhere else will return an undefined value.
-int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer);
-
-// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback.
-// Client are expected to call this function from their frame callback function
-// to get the deadline and use it to know whether a frame is likely to miss
-// presentation. Calling this function from anywhere else will return an undefined
-// value.
-int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer);
-
// Returns the current interval in ns between frames.
// Client are expected to call this function from their frame callback function.
// Calling this function from anywhere else will return an undefined value.
@@ -63,11 +50,26 @@ void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback,
void* data, uint32_t delayMillis);
+void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data);
void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data);
void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data);
+size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data);
+size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data);
+AVsyncId AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(
+ const AChoreographerFrameCallbackData* data, size_t index);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(
+ const AChoreographerFrameCallbackData* data, size_t index);
} // namespace android
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
index bd94b5523e..0f449028ac 100644
--- a/libs/nativedisplay/include/apex/display.h
+++ b/libs/nativedisplay/include/apex/display.h
@@ -107,11 +107,6 @@ void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outData
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);
/**
- * Queries the density for a given display configuration.
- */
-float ADisplayConfig_getDensity(ADisplayConfig* config);
-
-/**
* Queries the width in pixels for a given display configuration.
*/
int32_t ADisplayConfig_getWidth(ADisplayConfig* config);
diff --git a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
index 35ae3d2144..6fd4b8fff4 100644
--- a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
+++ b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
@@ -42,7 +42,8 @@ public:
typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- bool* outQueueEmpty, SurfaceTexture& cb,
+ HdrMetadata* outHdrMetadata, bool* outQueueEmpty,
+ SurfaceTexture& cb,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle);
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 6eaa84e225..0f119f3fc1 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -272,10 +272,11 @@ public:
status_t attachToContext(uint32_t tex);
sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outQueueEmpty,
+ HdrMetadata* outHdrMetadata, float* outTransformMatrix,
+ uint32_t* outTransform, bool* outQueueEmpty,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
- void* fencePassThroughHandle);
+ void* fencePassThroughHandle, ARect* currentCrop);
/**
* takeConsumerOwnership attaches a SurfaceTexture that is currently in the
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 85fe42f6fd..2987f3a87a 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -19,6 +19,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <android/hdr_metadata.h>
#include <jni.h>
#include <system/graphics.h>
@@ -82,12 +83,12 @@ typedef int (*ASurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle
* The caller gets ownership of the buffer and need to release it with
* AHardwareBuffer_release.
*/
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
- android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outNewContent,
- ASurfaceTexture_createReleaseFence createFence,
- ASurfaceTexture_fenceWait fenceWait,
- void* fencePassThroughHandle);
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(
+ ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace,
+ AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3,
+ android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform,
+ bool* outNewContent, ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* fencePassThroughHandle, ARect* currentCrop);
} // namespace android
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 9ed4915481..657931342d 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -7,6 +7,13 @@ LIBNATIVEDISPLAY {
AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
AChoreographer_registerRefreshRateCallback; # apex # introduced=30
AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
+ AChoreographer_postExtendedFrameCallback; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimeNanos; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelinesLength; # apex # introduced=33
+ AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos; # apex # introduced=33
AChoreographer_create; # apex # introduced=30
AChoreographer_destroy; # apex # introduced=30
AChoreographer_getFd; # apex # introduced=30
@@ -28,9 +35,14 @@ LIBNATIVEDISPLAY_PLATFORM {
android::AChoreographer_routePostFrameCallbackDelayed64*;
android::AChoreographer_routeRegisterRefreshRateCallback*;
android::AChoreographer_routeUnregisterRefreshRateCallback*;
+ android::AChoreographer_routePostExtendedFrameCallback*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimeNanos*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelinesLength*;
+ android::AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos*;
android::AChoreographer_signalRefreshRateCallbacks*;
- android::AChoreographer_getVsyncId*;
- android::AChoreographer_getFrameDeadline*;
android::AChoreographer_getFrameInterval*;
android::ADisplay_acquirePhysicalDisplays*;
android::ADisplay_release*;
@@ -38,7 +50,6 @@ LIBNATIVEDISPLAY_PLATFORM {
android::ADisplay_getDisplayType*;
android::ADisplay_getPreferredWideColorFormat*;
android::ADisplay_getCurrentConfig*;
- android::ADisplayConfig_getDensity*;
android::ADisplayConfig_getWidth*;
android::ADisplayConfig_getHeight*;
android::ADisplayConfig_getFps*;
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 365e788ea6..cf16739e89 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -28,7 +28,8 @@ void ImageConsumer::onReleaseBufferLocked(int buf) {
}
sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- bool* outQueueEmpty, SurfaceTexture& st,
+ HdrMetadata* outHdrMetadata, bool* outQueueEmpty,
+ SurfaceTexture& st,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle) {
@@ -121,6 +122,7 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace
st.computeCurrentTransformMatrixLocked();
*outDataspace = item.mDataSpace;
+ *outHdrMetadata = item.mHdrMetadata;
*outSlotid = slot;
return st.mSlots[slot].mGraphicBuffer;
}
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 62db6d069f..d3d4cbafdf 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -464,10 +464,12 @@ void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {
}
sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outQueueEmpty,
+ HdrMetadata* outHdrMetadata,
+ float* outTransformMatrix, uint32_t* outTransform,
+ bool* outQueueEmpty,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
- void* fencePassThroughHandle) {
+ void* fencePassThroughHandle, ARect* currentCrop) {
Mutex::Autolock _l(mMutex);
sp<GraphicBuffer> buffer;
@@ -481,9 +483,11 @@ sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspac
return buffer;
}
- buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
- createFence, fenceWait, fencePassThroughHandle);
+ buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outHdrMetadata, outQueueEmpty,
+ *this, createFence, fenceWait, fencePassThroughHandle);
memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+ *outTransform = mCurrentTransform;
+ *currentCrop = mCurrentCrop;
return buffer;
}
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index c214ab7718..39a925f712 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -192,17 +192,23 @@ void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
texture->consumer->releaseConsumerOwnership();
}
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
- android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outNewContent,
- ASurfaceTexture_createReleaseFence createFence,
- ASurfaceTexture_fenceWait fenceWait, void* handle) {
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(
+ ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace,
+ AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3,
+ android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform,
+ bool* outNewContent, ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* handle, ARect* currentCrop) {
sp<GraphicBuffer> buffer;
*outNewContent = false;
bool queueEmpty;
do {
- buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
- &queueEmpty, createFence, fenceWait, handle);
+ HdrMetadata metadata;
+ buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, &metadata, outTransformMatrix,
+ outTransform, &queueEmpty, createFence, fenceWait,
+ handle, currentCrop);
+ *outHdrType = static_cast<AHdrMetadataType>(metadata.validTypes);
+ *outCta861_3 = metadata.cta8613;
+ *outSmpte2086 = metadata.smpte2086;
if (!queueEmpty) {
*outNewContent = true;
}
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index e2f32e374a..cb3361b431 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -30,7 +30,7 @@
#include <private/android/AHardwareBufferHelpers.h>
#include <android/hardware/graphics/common/1.1/types.h>
-
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints
@@ -370,7 +370,7 @@ int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) {
if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
bool supported = false;
- GraphicBuffer* gBuffer = new GraphicBuffer();
+ sp<GraphicBuffer> gBuffer(new GraphicBuffer());
status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers,
desc->usage, &supported);
@@ -588,8 +588,12 @@ bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
"HAL and AHardwareBuffer pixel format don't match");
static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
"HAL and AHardwareBuffer pixel format don't match");
+ static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
+ AHARDWAREBUFFER_FORMAT_R8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
@@ -641,6 +645,8 @@ bool AHardwareBuffer_formatIsYuv(uint32_t format) {
uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+ return 1;
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
return 2;
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 75f2385174..18a4b2d3e8 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -133,16 +133,51 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo
int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) {
static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN));
- static_assert(static_cast<int>(ADATASPACE_SCRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SCRGB_LINEAR));
+ static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
+ static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
+ static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
+ static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
+ static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
+ static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
+ static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
+ static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
+ static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
+ static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
+ static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
+ static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
+ static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
+ static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
+ static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
+ static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
+ static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
+ static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
+ static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
+ static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
+ static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK));
+ static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
+ static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL));
+ static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
+ static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB));
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_PQ) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_ITU_PQ));
static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB));
+ static_assert(static_cast<int>(ADATASPACE_JFIF) == static_cast<int>(HAL_DATASPACE_V0_JFIF));
+ static_assert(static_cast<int>(ADATASPACE_BT601_625) == static_cast<int>(HAL_DATASPACE_V0_BT601_625));
+ static_assert(static_cast<int>(ADATASPACE_BT601_525) == static_cast<int>(HAL_DATASPACE_V0_BT601_525));
static_assert(static_cast<int>(ADATASPACE_BT2020) == static_cast<int>(HAL_DATASPACE_BT2020));
static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709));
static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3));
static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_HLG) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_HLG));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_HLG) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG));
+ static_assert(static_cast<int>(DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH));
+ static_assert(static_cast<int>(DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH));
if (!window || !query(window, NATIVE_WINDOW_IS_VALID) ||
!isDataSpaceValid(window, dataSpace)) {
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index e759513a63..771844f4fe 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -15,6 +15,13 @@
*/
/**
+ * @defgroup ADataSpace Data Space
+ *
+ * ADataSpace describes how to interpret colors.
+ * @{
+ */
+
+/**
* @file data_space.h
*/
@@ -43,6 +50,340 @@ enum ADataSpace {
ADATASPACE_UNKNOWN = 0,
/**
+ * Color-description aspects
+ *
+ * The following aspects define various characteristics of the color
+ * specification. These represent bitfields, so that a data space value
+ * can specify each of them independently.
+ */
+
+ /**
+ * Standard aspect
+ *
+ * Defines the chromaticity coordinates of the source primaries in terms of
+ * the CIE 1931 definition of x and y specified in ISO 11664-1.
+ */
+ STANDARD_MASK = 63 << 16,
+
+ /**
+ * Chromacity coordinates are unknown or are determined by the application.
+ * Implementations shall use the following suggested standards:
+ *
+ * All YCbCr formats: BT709 if size is 720p or larger (since most video
+ * content is letterboxed this corresponds to width is
+ * 1280 or greater, or height is 720 or greater).
+ * BT601_625 if size is smaller than 720p or is JPEG.
+ * All RGB formats: BT709.
+ *
+ * For all other formats standard is undefined, and implementations should use
+ * an appropriate standard for the data represented.
+ */
+ STANDARD_UNSPECIFIED = 0 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.300 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_BT709 = 1 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ */
+ STANDARD_BT601_625 = 2 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_BT601_625_UNADJUSTED = 3 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ */
+ STANDARD_BT601_525 = 4 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
+ * for RGB conversion (as in SMPTE 240M).
+ */
+ STANDARD_BT601_525_UNADJUSTED = 5 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_BT2020 = 6 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+ * for RGB conversion using the linear domain.
+ */
+ STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.21 0.71
+ * blue 0.14 0.08
+ * red 0.67 0.33
+ * white (C) 0.310 0.316
+ *
+ * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_BT470M = 8 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.243 0.692
+ * blue 0.145 0.049
+ * red 0.681 0.319
+ * white (C) 0.310 0.316
+ *
+ * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_FILM = 9 << 16,
+
+ /**
+ * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3)
+ * Primaries: x y
+ * green 0.265 0.690
+ * blue 0.150 0.060
+ * red 0.680 0.320
+ * white (D65) 0.3127 0.3290
+ */
+ STANDARD_DCI_P3 = 10 << 16,
+
+ /**
+ * Adobe RGB
+ * Primaries: x y
+ * green 0.210 0.710
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ */
+ STANDARD_ADOBE_RGB = 11 << 16,
+
+ /**
+ * Transfer aspect
+ *
+ * Transfer characteristics are the opto-electronic transfer characteristic
+ * at the source as a function of linear optical intensity (luminance).
+ *
+ * For digital signals, E corresponds to the recorded value. Normally, the
+ * transfer function is applied in RGB space to each of the R, G and B
+ * components independently. This may result in color shift that can be
+ * minized by applying the transfer function in Lab space only for the L
+ * component. Implementation may apply the transfer function in RGB space
+ * for all pixel formats if desired.
+ */
+ TRANSFER_MASK = 31 << 22,
+
+ /**
+ * Transfer characteristics are unknown or are determined by the
+ * application.
+ *
+ * Implementations should use the following transfer functions:
+ *
+ * For YCbCr formats: use TRANSFER_SMPTE_170M
+ * For RGB formats: use TRANSFER_SRGB
+ *
+ * For all other formats transfer function is undefined, and implementations
+ * should use an appropriate standard for the data represented.
+ */
+ TRANSFER_UNSPECIFIED = 0 << 22,
+
+ /**
+ * Transfer characteristic curve:
+ * E = L
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_LINEAR = 1 << 22,
+
+ /**
+ * Transfer characteristic curve:
+ *
+ * E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1
+ * = 12.92 * L for 0 <= L < 0.0031308
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_SRGB = 2 << 22,
+
+ /**
+ * BT.601 525, BT.601 625, BT.709, BT.2020
+ *
+ * Transfer characteristic curve:
+ * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
+ * = 4.500 * L for 0 <= L < 0.018
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_SMPTE_170M = 3 << 22,
+
+ /**
+ * Assumed display gamma 2.2.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.2)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_GAMMA2_2 = 4 << 22,
+
+ /**
+ * display gamma 2.6.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.6)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_GAMMA2_6 = 5 << 22,
+
+ /**
+ * display gamma 2.8.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.8)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_GAMMA2_8 = 6 << 22,
+
+ /**
+ * SMPTE ST 2084 (Dolby Perceptual Quantizer)
+ *
+ * Transfer characteristic curve:
+ * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
+ * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
+ * c2 = 32 * 2413 / 4096 = 18.8515625
+ * c3 = 32 * 2392 / 4096 = 18.6875
+ * m = 128 * 2523 / 4096 = 78.84375
+ * n = 0.25 * 2610 / 4096 = 0.1593017578125
+ * L - luminance of image 0 <= L <= 1 for HDR colorimetry.
+ * L = 1 corresponds to 10000 cd/m2
+ * E - corresponding electrical signal
+ */
+ TRANSFER_ST2084 = 7 << 22,
+
+ /**
+ * ARIB STD-B67 Hybrid Log Gamma
+ *
+ * Transfer characteristic curve:
+ * E = r * L^0.5 for 0 <= L <= 1
+ * = a * ln(L - b) + c for 1 < L
+ * a = 0.17883277
+ * b = 0.28466892
+ * c = 0.55991073
+ * r = 0.5
+ * L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
+ * to reference white level of 100 cd/m2
+ * E - corresponding electrical signal
+ */
+ TRANSFER_HLG = 8 << 22,
+
+ /**
+ * Range aspect
+ *
+ * Defines the range of values corresponding to the unit range of 0-1.
+ * This is defined for YCbCr only, but can be expanded to RGB space.
+ */
+ RANGE_MASK = 7 << 27,
+
+ /**
+ * Range is unknown or are determined by the application. Implementations
+ * shall use the following suggested ranges:
+ *
+ * All YCbCr formats: limited range.
+ * All RGB or RGBA formats (including RAW and Bayer): full range.
+ * All Y formats: full range
+ *
+ * For all other formats range is undefined, and implementations should use
+ * an appropriate range for the data represented.
+ */
+ RANGE_UNSPECIFIED = 0 << 27,
+
+ /**
+ * Full range uses all values for Y, Cb and Cr from
+ * 0 to 2^b-1, where b is the bit depth of the color format.
+ */
+ RANGE_FULL = 1 << 27,
+
+ /**
+ * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
+ * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of
+ * the color format.
+ *
+ * E.g. For 8-bit-depth formats:
+ * Luma (Y) samples should range from 16 to 235, inclusive
+ * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+ *
+ * For 10-bit-depth formats:
+ * Luma (Y) samples should range from 64 to 940, inclusive
+ * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
+ */
+ RANGE_LIMITED = 2 << 27,
+
+ /**
+ * Extended range is used for scRGB. Intended for use with
+ * floating point pixel formats. [0.0 - 1.0] is the standard
+ * sRGB space. Values outside the range 0.0 - 1.0 can encode
+ * color outside the sRGB gamut.
+ * Used to blend / merge multiple dataspaces on a single display.
+ */
+ RANGE_EXTENDED = 3 << 27,
+
+ /**
* scRGB linear encoding:
*
* The red, green, and blue components are stored in extended sRGB space,
@@ -103,6 +444,15 @@ enum ADataSpace {
ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
/**
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television
+ *
+ * Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
+ */
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+
+ /**
* Adobe RGB
*
* Use full range, gamma 2.2 transfer and Adobe RGB primaries
@@ -112,6 +462,33 @@ enum ADataSpace {
ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
/**
+ * JPEG File Interchange Format (JFIF)
+ *
+ * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
+ *
+ * Use full range, SMPTE 170M transfer and BT.601_625 standard.
+ */
+ ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
+
+ /**
+ * ITU-R Recommendation 601 (BT.601) - 525-line
+ *
+ * Standard-definition television, 525 Lines (NTSC)
+ *
+ * Use limited range, SMPTE 170M transfer and BT.601_525 standard.
+ */
+ ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+ /**
+ * ITU-R Recommendation 709 (BT.709)
+ *
+ * High-definition television
+ *
+ * Use limited range, SMPTE 170M transfer and BT.709 standard.
+ */
+ ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+ /**
* ITU-R Recommendation 2020 (BT.2020)
*
* Ultra High-definition television
@@ -151,8 +528,38 @@ enum ADataSpace {
* components.
*/
ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
+
+ /**
+ * Hybrid Log Gamma encoding:
+ *
+ * Use full range, hybrid log gamma transfer and BT2020 standard.
+ */
+ ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
+
+ /**
+ * ITU Hybrid Log Gamma encoding:
+ *
+ * Use limited range, hybrid log gamma transfer and BT2020 standard.
+ */
+ ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
+
+ /**
+ * Depth:
+ *
+ * This value is valid with formats HAL_PIXEL_FORMAT_Y16 and HAL_PIXEL_FORMAT_BLOB.
+ */
+ DEPTH = 4096,
+
+ /**
+ * ISO 16684-1:2011(E) Dynamic Depth:
+ *
+ * Embedded depth metadata following the dynamic depth specification.
+ */
+ DYNAMIC_DEPTH = 4098
};
__END_DECLS
#endif // ANDROID_DATA_SPACE_H
+
+/** @} */
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d93a84cd25..6f1f04df34 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -158,6 +158,13 @@ enum AHardwareBuffer_Format {
* cube-maps or multi-layered textures.
*/
AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8_UNORM
+ * OpenGL ES: GR_GL_R8
+ */
+ AHARDWAREBUFFER_FORMAT_R8_UNORM = 0x38,
};
/**
@@ -556,6 +563,7 @@ int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* _Nonnull buffer, uint64_t us
int32_t* _Nonnull outBytesPerPixel,
int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29);
+
/**
* Get the system wide unique id for an AHardwareBuffer.
*
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 7f0113500d..a319769148 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1025,10 +1025,11 @@ static inline int native_window_set_frame_rate(struct ANativeWindow* window, flo
}
static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
- int64_t frameTimelineVsyncId,
- int32_t inputEventId) {
- return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO,
- frameTimelineVsyncId, inputEventId);
+ int64_t frameTimelineVsyncId,
+ int32_t inputEventId,
+ int64_t startTimeNanos) {
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId,
+ inputEventId, startTimeNanos);
}
// ------------------------------------------------------------------------------------------------
@@ -1089,10 +1090,13 @@ static inline int ANativeWindow_getLastQueuedBuffer2(ANativeWindow* window,
/**
* Retrieves an identifier for the next frame to be queued by this window.
*
- * \return the next frame id.
+ * Frame ids start at 1 and are incremented on each new frame until the underlying surface changes,
+ * in which case the frame id is reset to 1.
+ *
+ * \return the next frame id (0 being uninitialized).
*/
-static inline int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
- int64_t value;
+static inline uint64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+ uint64_t value;
window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, &value);
return value;
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 570c7bc08d..07c5dd8a82 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -40,6 +40,11 @@ cc_defaults {
"libui",
"libutils",
],
+
+ static_libs: [
+ "libshaders",
+ "libtonemap",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
@@ -94,8 +99,10 @@ filegroup {
"skia/debug/SkiaCapture.cpp",
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
+ "skia/filters/GaussianBlurFilter.cpp",
+ "skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
- "skia/filters/StretchShaderFactory.cpp"
+ "skia/filters/StretchShaderFactory.cpp",
],
}
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
index eabff58eba..84771c0917 100644
--- a/libs/renderengine/ExternalTexture.cpp
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -14,30 +14,32 @@
* limitations under the License.
*/
-#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
#include <ui/GraphicBuffer.h>
#include "log/log_main.h"
-namespace android::renderengine {
+namespace android::renderengine::impl {
-ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine,
- uint32_t usage)
+ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer,
+ renderengine::RenderEngine& renderEngine, uint32_t usage)
: mBuffer(buffer), mRenderEngine(renderEngine) {
LOG_ALWAYS_FATAL_IF(buffer == nullptr,
"Attempted to bind a null buffer to an external texture!");
// GLESRenderEngine has a separate texture cache for output buffers,
- if (usage == Usage::WRITEABLE &&
- (mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::GLES ||
- mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::THREADED)) {
+ if (usage == WRITEABLE &&
+ (mRenderEngine.getRenderEngineType() ==
+ renderengine::RenderEngine::RenderEngineType::GLES ||
+ mRenderEngine.getRenderEngineType() ==
+ renderengine::RenderEngine::RenderEngineType::THREADED)) {
return;
}
- mRenderEngine.mapExternalTextureBuffer(mBuffer, usage & Usage::WRITEABLE);
+ mRenderEngine.mapExternalTextureBuffer(mBuffer, usage & WRITEABLE);
}
ExternalTexture::~ExternalTexture() {
mRenderEngine.unmapExternalTextureBuffer(mBuffer);
}
-} // namespace android::renderengine
+} // namespace android::renderengine::impl
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 9e466b6c34..c7ad058ab9 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -26,23 +26,7 @@
namespace android {
namespace renderengine {
-std::unique_ptr<RenderEngine> RenderEngine::create(RenderEngineCreationArgs args) {
- // Keep the ability to override by PROPERTIES:
- char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
- if (strcmp(prop, "gles") == 0) {
- args.renderEngineType = RenderEngineType::GLES;
- }
- if (strcmp(prop, "threaded") == 0) {
- args.renderEngineType = RenderEngineType::THREADED;
- }
- if (strcmp(prop, "skiagl") == 0) {
- args.renderEngineType = RenderEngineType::SKIA_GL;
- }
- if (strcmp(prop, "skiaglthreaded") == 0) {
- args.renderEngineType = RenderEngineType::SKIA_GL_THREADED;
- }
-
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
switch (args.renderEngineType) {
case RenderEngineType::THREADED:
ALOGD("Threaded RenderEngine with GLES Backend");
@@ -79,5 +63,16 @@ void RenderEngine::validateOutputBufferUsage(const sp<GraphicBuffer>& buffer) {
"output buffer not gpu writeable");
}
+std::future<RenderEngineResult> RenderEngine::drawLayers(
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) {
+ const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>();
+ std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
+ drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache,
+ std::move(bufferFence));
+ return resultFuture;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
new file mode 100644
index 0000000000..471159f390
--- /dev/null
+++ b/libs/renderengine/benchmark/Android.bp
@@ -0,0 +1,59 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_benchmark {
+ name: "librenderengine_bench",
+ defaults: [
+ "skia_deps",
+ "surfaceflinger_defaults",
+ ],
+ srcs: [
+ "main.cpp",
+ "Codec.cpp",
+ "Flags.cpp",
+ "RenderEngineBench.cpp",
+ ],
+ static_libs: [
+ "librenderengine",
+ "libshaders",
+ "libtonemap",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"RenderEngineBench\"",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libjnigraphics",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libprocessgroup",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+
+ data: ["resources/*"],
+}
diff --git a/libs/renderengine/benchmark/Codec.cpp b/libs/renderengine/benchmark/Codec.cpp
new file mode 100644
index 0000000000..80e4fc4432
--- /dev/null
+++ b/libs/renderengine/benchmark/Codec.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <RenderEngineBench.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+using namespace android;
+using namespace android::renderengine;
+
+namespace {
+struct DecoderDeleter {
+ void operator()(AImageDecoder* decoder) { AImageDecoder_delete(decoder); }
+};
+
+using AutoDecoderDeleter = std::unique_ptr<AImageDecoder, DecoderDeleter>;
+
+bool ok(int aImageDecoderResult, const char* path, const char* method) {
+ if (aImageDecoderResult == ANDROID_IMAGE_DECODER_SUCCESS) {
+ return true;
+ }
+
+ ALOGE("Failed AImageDecoder_%s on '%s' with error '%s'", method, path,
+ AImageDecoder_resultToString(aImageDecoderResult));
+ return false;
+}
+} // namespace
+
+namespace renderenginebench {
+
+void decode(const char* path, const sp<GraphicBuffer>& buffer) {
+ base::unique_fd fd{open(path, O_RDONLY)};
+ if (fd.get() < 0) {
+ ALOGE("Failed to open %s", path);
+ return;
+ }
+
+ AImageDecoder* decoder{nullptr};
+ auto result = AImageDecoder_createFromFd(fd.get(), &decoder);
+ if (!ok(result, path, "createFromFd")) {
+ return;
+ }
+
+ AutoDecoderDeleter deleter(decoder);
+
+ LOG_ALWAYS_FATAL_IF(buffer->getWidth() <= 0 || buffer->getHeight() <= 0,
+ "Impossible buffer size!");
+ auto width = static_cast<int32_t>(buffer->getWidth());
+ auto height = static_cast<int32_t>(buffer->getHeight());
+ result = AImageDecoder_setTargetSize(decoder, width, height);
+ if (!ok(result, path, "setTargetSize")) {
+ return;
+ }
+
+ void* pixels{nullptr};
+ int32_t stride{0};
+ if (auto status = buffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &pixels,
+ nullptr /*outBytesPerPixel*/, &stride);
+ status < 0) {
+ ALOGE("Failed to lock pixels!");
+ return;
+ }
+
+ result = AImageDecoder_decodeImage(decoder, pixels, static_cast<size_t>(stride),
+ static_cast<size_t>(stride * height));
+ if (auto status = buffer->unlock(); status < 0) {
+ ALOGE("Failed to unlock pixels!");
+ }
+
+ // For the side effect of logging.
+ (void)ok(result, path, "decodeImage");
+}
+
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer) {
+ base::unique_fd fd{open(path, O_WRONLY | O_CREAT, S_IWUSR)};
+ if (fd.get() < 0) {
+ ALOGE("Failed to open %s", path);
+ return;
+ }
+
+ void* pixels{nullptr};
+ int32_t stride{0};
+ if (auto status = buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels,
+ nullptr /*outBytesPerPixel*/, &stride);
+ status < 0) {
+ ALOGE("Failed to lock pixels!");
+ return;
+ }
+
+ AndroidBitmapInfo info{
+ .width = buffer->getWidth(),
+ .height = buffer->getHeight(),
+ .stride = static_cast<uint32_t>(stride),
+ .format = ANDROID_BITMAP_FORMAT_RGBA_8888,
+ .flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE,
+ };
+ int result = AndroidBitmap_compress(&info, ADATASPACE_SRGB, pixels,
+ ANDROID_BITMAP_COMPRESS_FORMAT_JPEG, 80, &fd,
+ [](void* fdPtr, const void* data, size_t size) -> bool {
+ const ssize_t bytesWritten =
+ write(reinterpret_cast<base::unique_fd*>(fdPtr)
+ ->get(),
+ data, size);
+ return bytesWritten > 0 &&
+ static_cast<size_t>(bytesWritten) == size;
+ });
+ if (result == ANDROID_BITMAP_RESULT_SUCCESS) {
+ ALOGD("Successfully encoded to '%s'", path);
+ } else {
+ ALOGE("Failed to encode to %s with error %d", path, result);
+ }
+
+ if (auto status = buffer->unlock(); status < 0) {
+ ALOGE("Failed to unlock pixels!");
+ }
+}
+
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/Flags.cpp b/libs/renderengine/benchmark/Flags.cpp
new file mode 100644
index 0000000000..c5d51563f4
--- /dev/null
+++ b/libs/renderengine/benchmark/Flags.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/log.h>
+#include <stdio.h>
+#include <string.h>
+
+namespace {
+bool gSave = false;
+}
+
+namespace renderenginebench {
+
+void parseFlagsForHelp(int argc, char** argv) {
+ for (int i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--help")) {
+ printf("RenderEngineBench-specific flags:\n");
+ printf("[--save]: Save the output to the device to confirm drawing result.\n");
+ break;
+ }
+ }
+}
+
+void parseFlags(int argc, char** argv) {
+ for (int i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--save")) {
+ gSave = true;
+ }
+ }
+}
+
+bool save() {
+ return gSave;
+}
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
new file mode 100644
index 0000000000..ead97cf5df
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <RenderEngineBench.h>
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/LayerSettings.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
+
+#include <mutex>
+
+using namespace android;
+using namespace android::renderengine;
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for Benchmark::Apply
+///////////////////////////////////////////////////////////////////////////////
+
+std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) {
+ switch (type) {
+ case RenderEngine::RenderEngineType::SKIA_GL_THREADED:
+ return "skiaglthreaded";
+ case RenderEngine::RenderEngineType::SKIA_GL:
+ return "skiagl";
+ case RenderEngine::RenderEngineType::GLES:
+ case RenderEngine::RenderEngineType::THREADED:
+ LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?");
+ return "unused";
+ }
+}
+
+/**
+ * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a
+ * Benchmark which specifies which RenderEngineType it uses.
+ *
+ * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make
+ * it obvious which version is being run.
+ *
+ * @param b The benchmark family
+ * @param type The type of RenderEngine to use.
+ */
+static void AddRenderEngineType(benchmark::internal::Benchmark* b,
+ RenderEngine::RenderEngineType type) {
+ b->Arg(static_cast<int64_t>(type));
+ b->ArgName(RenderEngineTypeName(type));
+}
+
+/**
+ * Run a benchmark once using SKIA_GL_THREADED.
+ */
+static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) {
+ AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for calling drawLayers
+///////////////////////////////////////////////////////////////////////////////
+
+std::pair<uint32_t, uint32_t> getDisplaySize() {
+ // These will be retrieved from a ui::Size, which stores int32_t, but they will be passed
+ // to GraphicBuffer, which wants uint32_t.
+ static uint32_t width, height;
+ std::once_flag once;
+ std::call_once(once, []() {
+ auto surfaceComposerClient = SurfaceComposerClient::getDefault();
+ auto displayToken = surfaceComposerClient->getInternalDisplayToken();
+ ui::DisplayMode displayMode;
+ if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
+ LOG_ALWAYS_FATAL("Failed to get active display mode!");
+ }
+ auto w = displayMode.resolution.width;
+ auto h = displayMode.resolution.height;
+ LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!");
+ width = static_cast<uint32_t>(w);
+ height = static_cast<uint32_t>(h);
+ });
+ return std::pair<uint32_t, uint32_t>(width, height);
+}
+
+// This value doesn't matter, as it's not read. TODO(b/199918329): Once we remove
+// GLESRenderEngine we can remove this, too.
+static constexpr const bool kUseFrameBufferCache = false;
+
+static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) {
+ auto args = RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setEnableProtectedContext(true)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(RenderEngine::ContextPriority::REALTIME)
+ .setRenderEngineType(type)
+ .setUseColorManagerment(true)
+ .build();
+ return RenderEngine::create(args);
+}
+
+static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re, uint32_t width,
+ uint32_t height,
+ uint64_t extraUsageFlags = 0,
+ std::string name = "output") {
+ return std::make_shared<
+ impl::ExternalTexture>(new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE |
+ extraUsageFlags,
+ std::move(name)),
+ re,
+ impl::ExternalTexture::Usage::READABLE |
+ impl::ExternalTexture::Usage::WRITEABLE);
+}
+
+static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re,
+ std::shared_ptr<ExternalTexture> original,
+ uint64_t extraUsageFlags, std::string name) {
+ const uint32_t width = original->getBuffer()->getWidth();
+ const uint32_t height = original->getBuffer()->getHeight();
+ auto texture = allocateBuffer(re, width, height, extraUsageFlags, name);
+
+ const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ };
+
+ const FloatRect layerRect(0, 0, width, height);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = original,
+ },
+ },
+ .alpha = half(1.0f),
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+
+ auto [status, drawFence] =
+ re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()).get();
+ sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence));
+ waitFence->waitForever(LOG_TAG);
+ return texture;
+}
+
+/**
+ * Helper for timing calls to drawLayers.
+ *
+ * Caller needs to create RenderEngine and the LayerSettings, and this takes
+ * care of setting up the display, starting and stopping the timer, calling
+ * drawLayers, and saving (if --save is used).
+ *
+ * This times both the CPU and GPU work initiated by drawLayers. All work done
+ * outside of the for loop is excluded from the timing measurements.
+ */
+static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& layers,
+ benchmark::State& benchState, const char* saveFileName) {
+ auto [width, height] = getDisplaySize();
+ auto outputBuffer = allocateBuffer(re, width, height);
+
+ const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ };
+
+ // This loop starts and stops the timer.
+ for (auto _ : benchState) {
+ auto [status, drawFence] = re.drawLayers(display, layers, outputBuffer,
+ kUseFrameBufferCache, base::unique_fd())
+ .get();
+ sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence));
+ waitFence->waitForever(LOG_TAG);
+ }
+
+ if (renderenginebench::save() && saveFileName) {
+ // Copy to a CPU-accessible buffer so we can encode it.
+ outputBuffer = copyBuffer(re, outputBuffer, GRALLOC_USAGE_SW_READ_OFTEN, "to_encode");
+
+ std::string outFile = base::GetExecutableDirectory();
+ outFile.append("/");
+ outFile.append(saveFileName);
+ outFile.append(".jpg");
+ renderenginebench::encodeToJpeg(outFile.c_str(), outputBuffer->getBuffer());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Benchmarks
+///////////////////////////////////////////////////////////////////////////////
+
+void BM_blur(benchmark::State& benchState) {
+ auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range()));
+
+ // Initially use cpu access so we can decode into it with AImageDecoder.
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer =
+ allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
+ {
+ std::string srcImage = base::GetExecutableDirectory();
+ srcImage.append("/resources/homescreen.png");
+ renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer());
+
+ // Now copy into GPU-only buffer for more realistic timing.
+ srcBuffer = copyBuffer(*re, srcBuffer, 0, "source");
+ }
+
+ const FloatRect layerRect(0, 0, width, height);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ },
+ },
+ .alpha = half(1.0f),
+ };
+ LayerSettings blurLayer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .alpha = half(1.0f),
+ .skipContentDraw = true,
+ .backgroundBlurRadius = 60,
+ };
+
+ auto layers = std::vector<LayerSettings>{layer, blurLayer};
+ benchDrawLayers(*re, layers, benchState, "blurred");
+}
+
+BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded);
diff --git a/libs/renderengine/benchmark/RenderEngineBench.h b/libs/renderengine/benchmark/RenderEngineBench.h
new file mode 100644
index 0000000000..1a25d77f00
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/GraphicBuffer.h>
+
+using namespace android;
+
+/**
+ * Utilities for running benchmarks.
+ */
+namespace renderenginebench {
+/**
+ * Parse RenderEngineBench-specific flags from the command line.
+ *
+ * --save Save the output buffer to a file to verify that it drew as
+ * expected.
+ */
+void parseFlags(int argc, char** argv);
+
+/**
+ * Parse flags for '--help'
+ */
+void parseFlagsForHelp(int argc, char** argv);
+
+/**
+ * Whether to save the drawing result to a file.
+ *
+ * True if --save was used on the command line.
+ */
+bool save();
+
+/**
+ * Decode the image at 'path' into 'buffer'.
+ *
+ * Currently only used for debugging. The image will be scaled to fit the
+ * buffer if necessary.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void decode(const char* path, const sp<GraphicBuffer>& buffer);
+
+/**
+ * Encode the buffer to a jpeg.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer);
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/main.cpp b/libs/renderengine/benchmark/main.cpp
new file mode 100644
index 0000000000..7a62853c9f
--- /dev/null
+++ b/libs/renderengine/benchmark/main.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <RenderEngineBench.h>
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+ // Initialize will exit if it sees '--help', so check for it and print info
+ // about our flags first.
+ renderenginebench::parseFlagsForHelp(argc, argv);
+ benchmark::Initialize(&argc, argv);
+
+ // Calling this separately from parseFlagsForHelp prevents collisions with
+ // google-benchmark's flags, since Initialize will consume and remove flags
+ // it recognizes.
+ renderenginebench::parseFlags(argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ return 0;
+}
diff --git a/libs/renderengine/benchmark/resources/homescreen.png b/libs/renderengine/benchmark/resources/homescreen.png
new file mode 100644
index 0000000000..997b72d7a3
--- /dev/null
+++ b/libs/renderengine/benchmark/resources/homescreen.png
Binary files differ
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 467f848237..22dd86698b 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1078,15 +1078,16 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer
return image;
}
-status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) {
+void GLESRenderEngine::drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) {
ATRACE_CALL();
if (layers.empty()) {
ALOGV("Drawing empty layer stack");
- return NO_ERROR;
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ return;
}
if (bufferFence.get() >= 0) {
@@ -1100,7 +1101,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
if (buffer == nullptr) {
ALOGE("No output buffer provided. Aborting GPU composition.");
- return BAD_VALUE;
+ resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+ return;
}
validateOutputBufferUsage(buffer->getBuffer());
@@ -1108,10 +1110,10 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
// Gathering layers that requested blur, we'll need them to decide when to render to an
// offscreen buffer, and when to render to the native buffer.
- std::deque<const LayerSettings*> blurLayers;
+ std::deque<const LayerSettings> blurLayers;
if (CC_LIKELY(mBlurFilter != nullptr)) {
- for (auto layer : layers) {
- if (layer->backgroundBlurRadius > 0) {
+ for (const auto& layer : layers) {
+ if (layer.backgroundBlurRadius > 0) {
blurLayers.push_back(layer);
}
}
@@ -1128,18 +1130,20 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
buffer->getBuffer()->handle);
checkErrors();
- return fbo->getStatus();
+ resultPromise->set_value({fbo->getStatus(), base::unique_fd()});
+ return;
}
setViewportAndProjection(display.physicalDisplay, display.clip);
} else {
setViewportAndProjection(display.physicalDisplay, display.clip);
auto status =
- mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
+ mBlurFilter->setAsDrawTarget(display, blurLayers.front().backgroundBlurRadius);
if (status != NO_ERROR) {
ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
buffer->getBuffer()->handle);
checkErrors();
- return status;
+ resultPromise->set_value({status, base::unique_fd()});
+ return;
}
}
@@ -1156,10 +1160,6 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
const mat4 projectionMatrix =
ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
- if (!display.clearRegion.isEmpty()) {
- glDisable(GL_BLEND);
- fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
- }
Mesh mesh = Mesh::Builder()
.setPrimitive(Mesh::TRIANGLE_FAN)
@@ -1167,7 +1167,7 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
.setTexCoords(2 /* size */)
.setCropCoords(2 /* size */)
.build();
- for (auto const layer : layers) {
+ for (const auto& layer : layers) {
if (blurLayers.size() > 0 && blurLayers.front() == layer) {
blurLayers.pop_front();
@@ -1176,7 +1176,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
buffer->getBuffer()->handle);
checkErrors("Can't render first blur pass");
- return status;
+ resultPromise->set_value({status, base::unique_fd()});
+ return;
}
if (blurLayers.size() == 0) {
@@ -1192,13 +1193,14 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
// There's still something else to blur, so let's keep rendering to our FBO
// instead of to the display.
status = mBlurFilter->setAsDrawTarget(display,
- blurLayers.front()->backgroundBlurRadius);
+ blurLayers.front().backgroundBlurRadius);
}
if (status != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
buffer->getBuffer()->handle);
checkErrors("Can't bind native framebuffer");
- return status;
+ resultPromise->set_value({status, base::unique_fd()});
+ return;
}
status = mBlurFilter->render(blurLayersSize > 1);
@@ -1206,47 +1208,48 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
buffer->getBuffer()->handle);
checkErrors("Can't render blur filter");
- return status;
+ resultPromise->set_value({status, base::unique_fd()});
+ return;
}
}
// Ensure luminance is at least 100 nits to avoid div-by-zero
- const float maxLuminance = std::max(100.f, layer->source.buffer.maxLuminanceNits);
+ const float maxLuminance = std::max(100.f, layer.source.buffer.maxLuminanceNits);
mState.maxMasteringLuminance = maxLuminance;
mState.maxContentLuminance = maxLuminance;
- mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform;
+ mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
- const FloatRect bounds = layer->geometry.boundaries;
+ const FloatRect bounds = layer.geometry.boundaries;
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
position[0] = vec2(bounds.left, bounds.top);
position[1] = vec2(bounds.left, bounds.bottom);
position[2] = vec2(bounds.right, bounds.bottom);
position[3] = vec2(bounds.right, bounds.top);
- setupLayerCropping(*layer, mesh);
- setColorTransform(layer->colorTransform);
+ setupLayerCropping(layer, mesh);
+ setColorTransform(layer.colorTransform);
bool usePremultipliedAlpha = true;
bool disableTexture = true;
bool isOpaque = false;
- if (layer->source.buffer.buffer != nullptr) {
+ if (layer.source.buffer.buffer != nullptr) {
disableTexture = false;
- isOpaque = layer->source.buffer.isOpaque;
+ isOpaque = layer.source.buffer.isOpaque;
- sp<GraphicBuffer> gBuf = layer->source.buffer.buffer->getBuffer();
+ sp<GraphicBuffer> gBuf = layer.source.buffer.buffer->getBuffer();
validateInputBufferUsage(gBuf);
- bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
- layer->source.buffer.fence);
+ bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
+ layer.source.buffer.fence);
- usePremultipliedAlpha = layer->source.buffer.usePremultipliedAlpha;
- Texture texture(Texture::TEXTURE_EXTERNAL, layer->source.buffer.textureName);
- mat4 texMatrix = layer->source.buffer.textureTransform;
+ usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+ Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+ mat4 texMatrix = layer.source.buffer.textureTransform;
texture.setMatrix(texMatrix.asArray());
- texture.setFiltering(layer->source.buffer.useTextureFiltering);
+ texture.setFiltering(layer.source.buffer.useTextureFiltering);
texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
- setSourceY410BT2020(layer->source.buffer.isY410BT2020);
+ setSourceY410BT2020(layer.source.buffer.isY410BT2020);
renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
texCoords[0] = vec2(0.0, 0.0);
@@ -1261,62 +1264,63 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
}
}
- const half3 solidColor = layer->source.solidColor;
- const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer->alpha);
+ const half3 solidColor = layer.source.solidColor;
+ const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
// Buffer sources will have a black solid color ignored in the shader,
// so in that scenario the solid color passed here is arbitrary.
setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer->geometry.roundedCornersRadius);
- if (layer->disableBlending) {
+ layer.geometry.roundedCornersRadius);
+ if (layer.disableBlending) {
glDisable(GL_BLEND);
}
- setSourceDataSpace(layer->sourceDataspace);
+ setSourceDataSpace(layer.sourceDataspace);
- if (layer->shadow.length > 0.0f) {
- handleShadow(layer->geometry.boundaries, layer->geometry.roundedCornersRadius,
- layer->shadow);
+ if (layer.shadow.length > 0.0f) {
+ handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
+ layer.shadow);
}
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
- else if (layer->geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
- handleRoundedCorners(display, *layer, mesh);
+ else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, layer, mesh);
} else {
drawMesh(mesh);
}
// Cleanup if there's a buffer source
- if (layer->source.buffer.buffer != nullptr) {
+ if (layer.source.buffer.buffer != nullptr) {
disableBlending();
setSourceY410BT2020(false);
disableTexturing();
}
}
- if (drawFence != nullptr) {
- *drawFence = flush();
- }
+ base::unique_fd drawFence = flush();
+
// If flush failed or we don't support native fences, we need to force the
// gl command stream to be executed.
- if (drawFence == nullptr || drawFence->get() < 0) {
+ if (drawFence.get() < 0) {
bool success = finish();
if (!success) {
ALOGE("Failed to flush RenderEngine commands");
checkErrors();
// Chances are, something illegal happened (either the caller passed
// us bad parameters, or we messed up our shader generation).
- return INVALID_OPERATION;
+ resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+ return;
}
mLastDrawFence = nullptr;
} else {
// The caller takes ownership of drawFence, so we need to duplicate the
// fd here.
- mLastDrawFence = new Fence(dup(drawFence->get()));
+ mLastDrawFence = new Fence(dup(drawFence.get()));
}
mPriorResourcesCleaned = false;
checkErrors();
- return NO_ERROR;
+ resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+ return;
}
void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 14627ce0ce..1d7c2cafb5 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -63,11 +63,6 @@ public:
bool isProtected() const override { return mInProtectedContext; }
bool supportsProtectedContent() const override;
void useProtectedContext(bool useProtectedContext) override;
- status_t drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) override;
void cleanupPostRender() override;
int getContextPriority() override;
bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
@@ -107,6 +102,11 @@ protected:
EXCLUDES(mRenderingMutex);
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
bool canSkipPostRenderCleanup() const override;
+ void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
private:
friend class BindNativeBufferAsFramebuffer;
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 53fa622ad8..b4cab39bd9 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -51,25 +51,22 @@ struct DisplaySettings {
// dataspace, in non-linear space.
mat4 colorTransform = mat4();
- // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
- // This is specified in layer-stack space.
- Region clearRegion = Region::INVALID_REGION;
-
// An additional orientation flag to be applied after clipping the output.
// By way of example, this may be used for supporting fullscreen screenshot
// capture of a device in landscape while the buffer is in portrait
// orientation.
uint32_t orientation = ui::Transform::ROT_0;
- // SDR white point, -1f if unknown
- float sdrWhitePointNits = -1.f;
+ // Target luminance of the display. -1f if unknown.
+ // All layers will be dimmed by (max(layer white points) / targetLuminanceNits).
+ // If the target luminance is unknown, then no display-level dimming occurs.
+ float targetLuminanceNits = -1.f;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
lhs.maxLuminance == rhs.maxLuminance && lhs.outputDataspace == rhs.outputDataspace &&
- lhs.colorTransform == rhs.colorTransform &&
- lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+ lhs.colorTransform == rhs.colorTransform && lhs.orientation == rhs.orientation;
}
// Defining PrintTo helps with Google Tests.
@@ -84,9 +81,6 @@ static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os)
PrintTo(settings.outputDataspace, os);
*os << "\n .colorTransform = " << settings.colorTransform;
*os << "\n .clearRegion = ";
- PrintTo(settings.clearRegion, os);
- *os << "\n .orientation = " << settings.orientation;
- *os << "\n}";
}
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/ExternalTexture.h b/libs/renderengine/include/renderengine/ExternalTexture.h
index 07f0833d4a..621a209afa 100644
--- a/libs/renderengine/include/renderengine/ExternalTexture.h
+++ b/libs/renderengine/include/renderengine/ExternalTexture.h
@@ -33,28 +33,22 @@ class RenderEngine;
*/
class ExternalTexture {
public:
- // Usage specifies the rendering intent for the buffer.
- enum Usage : uint32_t {
- // When a buffer is not READABLE but is WRITEABLE, then GLESRenderEngine will use that as a
- // hint to load the buffer into a separate cache
- READABLE = 1 << 0,
-
- // The buffer needs to be mapped as a 2D texture if set, otherwise must be mapped as an
- // external texture
- WRITEABLE = 1 << 1,
- };
- // Creates an ExternalTexture for the provided buffer and RenderEngine instance, with the given
- // usage hint of type Usage.
- ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine, uint32_t usage);
-
- ~ExternalTexture();
+ ExternalTexture() = default;
+ virtual ~ExternalTexture() = default;
+
+ virtual bool hasSameBuffer(const ExternalTexture& other) const = 0;
+ virtual uint32_t getWidth() const = 0;
+ virtual uint32_t getHeight() const = 0;
+ virtual uint64_t getId() const = 0;
+ virtual PixelFormat getPixelFormat() const = 0;
+ virtual uint64_t getUsage() const = 0;
// Retrieves the buffer that is bound to this texture.
- const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ virtual const sp<GraphicBuffer>& getBuffer() const = 0;
-private:
- sp<GraphicBuffer> mBuffer;
- RenderEngine& mRenderEngine;
+ Rect getBounds() const {
+ return {0, 0, static_cast<int32_t>(getWidth()), static_cast<int32_t>(getHeight())};
+ }
DISALLOW_COPY_AND_ASSIGN(ExternalTexture);
};
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 715f3f84ae..171cbaa2ef 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -171,6 +171,12 @@ struct LayerSettings {
// Name associated with the layer for debugging purposes.
std::string name;
+
+ // Luminance of the white point for this layer. Used for linear dimming.
+ // Individual layers will be dimmed by (whitePointNits / maxWhitePoint).
+ // If white point nits are unknown, then this layer is assumed to have the
+ // same luminance as the brightest layer in the scene.
+ float whitePointNits = -1.f;
};
// Keep in sync with custom comparison function in
@@ -307,6 +313,7 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
PrintTo(settings.shadow, os);
*os << "\n .stretchEffect = ";
PrintTo(settings.stretchEffect, os);
+ *os << "\n .whitePointNits = " << settings.whitePointNits;
*os << "\n}";
}
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index f555cdbc0d..faa84fc1cd 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -68,6 +68,7 @@ class Image;
class Mesh;
class Texture;
struct RenderEngineCreationArgs;
+struct RenderEngineResult;
namespace threaded {
class RenderEngineThreaded;
@@ -75,6 +76,7 @@ class RenderEngineThreaded;
namespace impl {
class RenderEngine;
+class ExternalTexture;
}
enum class Protection {
@@ -98,7 +100,7 @@ public:
SKIA_GL_THREADED = 4,
};
- static std::unique_ptr<RenderEngine> create(RenderEngineCreationArgs args);
+ static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
virtual ~RenderEngine() = 0;
@@ -156,17 +158,12 @@ public:
// parameter does nothing.
// @param bufferFence Fence signalling that the buffer is ready to be drawn
// to.
- // @param drawFence A pointer to a fence, which will fire when the buffer
- // has been drawn to and is ready to be examined. The fence will be
- // initialized by this method. The caller will be responsible for owning the
- // fence.
- // @return An error code indicating whether drawing was successful. For
- // now, this always returns NO_ERROR.
- virtual status_t drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) = 0;
+ // @return A future object of RenderEngineResult struct indicating whether
+ // drawing was successful in async mode.
+ virtual std::future<RenderEngineResult> drawLayers(
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence);
// Clean-up method that should be called on the main thread after the
// drawFence returned by drawLayers fires. This method will free up
@@ -193,6 +190,10 @@ public:
static void validateInputBufferUsage(const sp<GraphicBuffer>&);
static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
+ // Allows flinger to get the render engine thread id for power management with ADPF
+ // Returns the tid of the renderengine thread if it's threaded, and std::nullopt otherwise
+ virtual std::optional<pid_t> getRenderEngineTid() const { return std::nullopt; }
+
protected:
RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
@@ -228,10 +229,16 @@ protected:
// avoid any thread synchronization that may be required by directly calling postRenderCleanup.
virtual bool canSkipPostRenderCleanup() const = 0;
- friend class ExternalTexture;
+ friend class impl::ExternalTexture;
friend class threaded::RenderEngineThreaded;
friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
const RenderEngineType mRenderEngineType;
+
+ virtual void drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) = 0;
};
struct RenderEngineCreationArgs {
@@ -318,6 +325,13 @@ private:
RenderEngine::RenderEngineType::SKIA_GL_THREADED;
};
+struct RenderEngineResult {
+ // status indicates if drawing is successful
+ status_t status;
+ // drawFence will fire when the buffer has been drawn to and is ready to be examined.
+ base::unique_fd drawFence;
+};
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/impl/ExternalTexture.h b/libs/renderengine/include/renderengine/impl/ExternalTexture.h
new file mode 100644
index 0000000000..c0e24f0c10
--- /dev/null
+++ b/libs/renderengine/include/renderengine/impl/ExternalTexture.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <renderengine/ExternalTexture.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::renderengine::impl {
+
+class RenderEngine;
+
+class ExternalTexture : public android::renderengine::ExternalTexture {
+public:
+ // Usage specifies the rendering intent for the buffer.
+ enum Usage : uint32_t {
+ // When a buffer is not READABLE but is WRITEABLE, then GLESRenderEngine will use that as a
+ // hint to load the buffer into a separate cache
+ READABLE = 1 << 0,
+
+ // The buffer needs to be mapped as a 2D texture if set, otherwise must be mapped as an
+ // external texture
+ WRITEABLE = 1 << 1,
+ };
+
+ // Creates an ExternalTexture for the provided buffer and RenderEngine instance, with the given
+ // usage hint of type Usage.
+ ExternalTexture(const sp<GraphicBuffer>& buffer,
+ android::renderengine::RenderEngine& renderEngine, uint32_t usage);
+ ~ExternalTexture();
+ const sp<GraphicBuffer>& getBuffer() const override { return mBuffer; };
+ uint32_t getWidth() const override { return getBuffer()->getWidth(); }
+ uint32_t getHeight() const override { return getBuffer()->getHeight(); }
+ uint64_t getId() const override { return getBuffer()->getId(); }
+ PixelFormat getPixelFormat() const override { return getBuffer()->getPixelFormat(); }
+ uint64_t getUsage() const override { return getBuffer()->getUsage(); }
+ bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+ return getBuffer() == other.getBuffer();
+ }
+
+private:
+ sp<GraphicBuffer> mBuffer;
+ android::renderengine::RenderEngine& mRenderEngine;
+};
+
+} // namespace android::renderengine::impl
diff --git a/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h b/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h
new file mode 100644
index 0000000000..974e0fddde
--- /dev/null
+++ b/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <renderengine/ExternalTexture.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+class FakeExternalTexture : public renderengine::ExternalTexture {
+ const sp<GraphicBuffer> mNullBuffer = nullptr;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint64_t mId;
+ PixelFormat mPixelFormat;
+ uint64_t mUsage;
+
+public:
+ FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
+ uint64_t usage)
+ : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
+ const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; }
+ bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+ return getId() == other.getId();
+ }
+ uint32_t getWidth() const override { return mWidth; }
+ uint32_t getHeight() const override { return mHeight; }
+ uint64_t getId() const override { return mId; }
+ PixelFormat getPixelFormat() const override { return mPixelFormat; }
+ uint64_t getUsage() const override { return mUsage; }
+ ~FakeExternalTexture() = default;
+};
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 0be3ba6b11..248bd652c0 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -47,10 +47,15 @@ public:
MOCK_METHOD1(useProtectedContext, void(bool));
MOCK_METHOD0(cleanupPostRender, void());
MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
- MOCK_METHOD6(drawLayers,
- status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
- const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
- base::unique_fd*));
+ MOCK_METHOD5(drawLayers,
+ std::future<RenderEngineResult>(const DisplaySettings&,
+ const std::vector<LayerSettings>&,
+ const std::shared_ptr<ExternalTexture>&,
+ const bool, base::unique_fd&&));
+ MOCK_METHOD6(drawLayersInternal,
+ void(const std::shared_ptr<std::promise<RenderEngineResult>>&&,
+ const DisplaySettings&, const std::vector<LayerSettings>&,
+ const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&));
MOCK_METHOD0(cleanFramebufferCache, void());
MOCK_METHOD0(getContextPriority, int());
MOCK_METHOD0(supportsBackgroundBlur, bool());
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index cc627b8bc8..a3a1969ee7 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -19,6 +19,7 @@
#include "android-base/unique_fd.h"
#include "renderengine/DisplaySettings.h"
#include "renderengine/LayerSettings.h"
+#include "renderengine/impl/ExternalTexture.h"
#include "ui/GraphicBuffer.h"
#include "ui/GraphicTypes.h"
#include "ui/PixelFormat.h"
@@ -95,25 +96,27 @@ static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettin
.alpha = 1,
};
- auto layers = std::vector<const LayerSettings*>{&layer, &caster};
- // When sourceDataspace matches dest, the general shadow fragment shader doesn't
- // have color correction added.
- // independently, when it is not srgb, the *vertex* shader has color correction added.
- // This may be a bug, but the shader still needs to be cached as it is triggered
- // during youtube pip.
- for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
- layer.sourceDataspace = dataspace;
- // The 2nd matrix, which has different scales for x and y, will
- // generate the slower (more general case) shadow shader
- for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) {
- layer.geometry.positionTransform = transform;
- caster.geometry.positionTransform = transform;
- for (bool translucent : {false, true}){
- layer.shadow.casterIsTranslucent = translucent;
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
- }
- }
+ auto layers = std::vector<LayerSettings>{layer, caster};
+ // Four combinations of settings are used (two transforms here, and drawShadowLayers is
+ // called with two different destination data spaces) They're all rounded rect.
+ // Three of these are cache misses that generate new shaders.
+ // The first combination generates a short and simple shadow shader.
+ // The second combination, flip transform, generates two shaders. The first appears to involve
+ // gaussian_fp. The second is a long and general purpose shadow shader with a device space
+ // transformation stage.
+ // The third combination is a cache hit, nothing new.
+ // The fourth combination, flip transform with a non-SRGB destination dataspace, is new.
+ // It is unique in that nearly everything is done in the vertex shader, and that vertex shader
+ // requires color correction. This is triggered differently from every other instance of color
+ // correction. All other instances are triggered when src and dst dataspaces differ, while
+ // this one is triggered by the destination being non-srgb. Apparently since the third
+ // combination is a cache hit, this color correction is only added when the vertex shader is
+ // doing something non-trivial.
+ for (auto transform : {mat4(), kFlip}) {
+ layer.geometry.positionTransform = transform;
+ caster.geometry.positionTransform = transform;
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd());
}
}
@@ -138,7 +141,7 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting
}},
};
- auto layers = std::vector<const LayerSettings*>{&layer};
+ auto layers = std::vector<LayerSettings>{layer};
for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
layer.sourceDataspace = dataspace;
// Cache shaders for both rects and round rects.
@@ -151,7 +154,7 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting
for (auto alpha : {half(.2f), half(1.0f)}) {
layer.alpha = alpha;
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ base::unique_fd());
}
}
}
@@ -174,13 +177,13 @@ static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySetting
.alpha = 0.5,
};
- auto layers = std::vector<const LayerSettings*>{&layer};
+ auto layers = std::vector<LayerSettings>{layer};
for (auto transform : {mat4(), kScaleAndTranslate}) {
layer.geometry.positionTransform = transform;
for (float roundedCornersRadius : {0.0f, 50.f}) {
layer.geometry.roundedCornersRadius = roundedCornersRadius;
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ base::unique_fd());
}
}
}
@@ -199,12 +202,12 @@ static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings
.skipContentDraw = true,
};
- auto layers = std::vector<const LayerSettings*>{&layer};
+ auto layers = std::vector<LayerSettings>{layer};
// Different blur code is invoked for radii less and greater than 30 pixels
for (int radius : {9, 60}) {
layer.backgroundBlurRadius = radius;
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ base::unique_fd());
}
}
@@ -240,7 +243,7 @@ static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySetti
},
};
- auto layers = std::vector<const LayerSettings*>{&layer};
+ auto layers = std::vector<LayerSettings>{layer};
for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {
layer.source = pixelSource;
for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
@@ -251,7 +254,7 @@ static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySetti
for (float alpha : {0.5f, 1.f}) {
layer.alpha = alpha,
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ base::unique_fd());
}
}
}
@@ -287,9 +290,8 @@ static void drawPIPImageLayer(SkiaRenderEngine* renderengine, const DisplaySetti
};
- auto layers = std::vector<const LayerSettings*>{&layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ auto layers = std::vector<LayerSettings>{layer};
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
}
static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -316,9 +318,8 @@ static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySett
};
- auto layers = std::vector<const LayerSettings*>{&layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ auto layers = std::vector<LayerSettings>{layer};
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
}
//
@@ -365,8 +366,8 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
1, usage, "primeShaderCache_dst");
const auto dstTexture =
- std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
- ExternalTexture::Usage::WRITEABLE);
+ std::make_shared<impl::ExternalTexture>(dstBuffer, *renderengine,
+ impl::ExternalTexture::Usage::WRITEABLE);
// This buffer will be the source for the call to drawImageLayers. Draw
// something to it as a placeholder for what an app draws. We should draw
// something, but the details are not important. Make use of the shadow layer drawing step
@@ -375,12 +376,13 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
1, usage, "drawImageLayer_src");
- const auto srcTexture =
- std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
- ExternalTexture::Usage::READABLE |
- ExternalTexture::Usage::WRITEABLE);
+ const auto srcTexture = std::make_shared<
+ impl::ExternalTexture>(srcBuffer, *renderengine,
+ impl::ExternalTexture::Usage::READABLE |
+ impl::ExternalTexture::Usage::WRITEABLE);
drawHolePunchLayer(renderengine, display, dstTexture);
drawSolidLayers(renderengine, display, dstTexture);
+
drawShadowLayers(renderengine, display, srcTexture);
drawShadowLayers(renderengine, p3Display, srcTexture);
@@ -397,8 +399,8 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
1, usageExternal, "primeShaderCache_external");
const auto externalTexture =
- std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
- ExternalTexture::Usage::READABLE);
+ std::make_shared<impl::ExternalTexture>(externalBuffer, *renderengine,
+ impl::ExternalTexture::Usage::READABLE);
std::vector<const std::shared_ptr<ExternalTexture>> textures =
{srcTexture, externalTexture};
@@ -411,8 +413,8 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
status_t error = f16ExternalBuffer->initCheck();
if (!error) {
const auto f16ExternalTexture =
- std::make_shared<ExternalTexture>(f16ExternalBuffer, *renderengine,
- ExternalTexture::Usage::READABLE);
+ std::make_shared<impl::ExternalTexture>(f16ExternalBuffer, *renderengine,
+ impl::ExternalTexture::Usage::READABLE);
textures.push_back(f16ExternalTexture);
}
@@ -424,6 +426,16 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
+ // draw one final layer synchronously to force GL submit
+ LayerSettings layer{
+ .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)},
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+ // call get() to make it synchronous
+ renderengine
+ ->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd())
+ .get();
+
const nsecs_t timeAfter = systemTime();
const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
const int shadersCompiled = renderengine->reportShadersCompiled();
diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp
index ff4d348f0d..f367a84946 100644
--- a/libs/renderengine/skia/ColorSpaces.cpp
+++ b/libs/renderengine/skia/ColorSpaces.cpp
@@ -20,6 +20,7 @@ namespace android {
namespace renderengine {
namespace skia {
+// please keep in sync with hwui/utils/Color.cpp
sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
skcms_Matrix3x3 gamut;
switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
@@ -32,6 +33,17 @@ sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
case HAL_DATASPACE_STANDARD_DCI_P3:
gamut = SkNamedGamut::kDisplayP3;
break;
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ gamut = SkNamedGamut::kAdobeRGB;
+ break;
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ case HAL_DATASPACE_STANDARD_BT470M:
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
default:
gamut = SkNamedGamut::kSRGB;
break;
@@ -42,10 +54,19 @@ sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
case HAL_DATASPACE_TRANSFER_SRGB:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
case HAL_DATASPACE_TRANSFER_ST2084:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
case HAL_DATASPACE_TRANSFER_HLG:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+ case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
default:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index e42b5b9e79..cc90946753 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -46,6 +46,7 @@
#include <cmath>
#include <cstdint>
#include <memory>
+#include <numeric>
#include "../gl/GLExtensions.h"
#include "Cache.h"
@@ -53,6 +54,8 @@
#include "SkBlendMode.h"
#include "SkImageInfo.h"
#include "filters/BlurFilter.h"
+#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
@@ -328,7 +331,7 @@ SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGL
if (args.supportsBackgroundBlur) {
ALOGD("Background Blurs Enabled");
- mBlurFilter = new BlurFilter();
+ mBlurFilter = new KawaseBlurFilter();
}
mCapture = std::make_unique<SkiaCapture>();
}
@@ -611,31 +614,32 @@ private:
};
sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
- sk_sp<SkShader> shader,
- const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
- bool requiresLinearEffect) {
- const auto stretchEffect = layer->stretchEffect;
+ const RuntimeEffectShaderParameters& parameters) {
// The given surface will be stretched by HWUI via matrix transformation
// which gets similar results for most surfaces
// Determine later on if we need to leverage the stertch shader within
// surface flinger
+ const auto& stretchEffect = parameters.layer.stretchEffect;
+ auto shader = parameters.shader;
if (stretchEffect.hasEffect()) {
- const auto targetBuffer = layer->source.buffer.buffer;
+ const auto targetBuffer = parameters.layer.source.buffer.buffer;
const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- if (graphicBuffer && shader) {
+ if (graphicBuffer && parameters.shader) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
}
}
- if (requiresLinearEffect) {
- const ui::Dataspace inputDataspace =
- mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
- const ui::Dataspace outputDataspace =
- mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
+ if (parameters.requiresLinearEffect) {
+ const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
+ const ui::Dataspace outputDataspace = mUseColorManagement
+ ? parameters.display.outputDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
- LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
- .outputDataspace = outputDataspace,
- .undoPremultipliedAlpha = undoPremultipliedAlpha};
+ auto effect =
+ shaders::LinearEffect{.inputDataspace = inputDataspace,
+ .outputDataspace = outputDataspace,
+ .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};
auto effectIter = mRuntimeEffects.find(effect);
sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
@@ -645,16 +649,16 @@ sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
} else {
runtimeEffect = effectIter->second;
}
- float maxLuminance = layer->source.buffer.maxLuminanceNits;
- // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
- // white point
- if (maxLuminance <= 0.f) {
- maxLuminance = display.sdrWhitePointNits;
- }
- return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
- display.maxLuminance, maxLuminance);
+ mat4 colorTransform = parameters.layer.colorTransform;
+
+ colorTransform *=
+ mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
+ parameters.layerDimmingRatio, 1.f));
+ return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
+ parameters.display.maxLuminance,
+ parameters.layer.source.buffer.maxLuminanceNits);
}
- return shader;
+ return parameters.shader;
}
void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
@@ -727,22 +731,29 @@ static SkRRect getBlurRRect(const BlurRegion& region) {
return roundedRect;
}
-status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool /*useFramebufferCache*/,
- base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+static bool equalsWithinMargin(float expected, float value, float margin) {
+ LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!");
+ return std::abs(expected - value) < margin;
+}
+
+void SkiaGLRenderEngine::drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
+ base::unique_fd&& bufferFence) {
ATRACE_NAME("SkiaGL::drawLayers");
std::lock_guard<std::mutex> lock(mRenderingMutex);
if (layers.empty()) {
ALOGV("Drawing empty layer stack");
- return NO_ERROR;
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ return;
}
if (buffer == nullptr) {
ALOGE("No output buffer provided. Aborting GPU composition.");
- return BAD_VALUE;
+ resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+ return;
}
validateOutputBufferUsage(buffer->getBuffer());
@@ -774,7 +785,8 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
if (dstCanvas == nullptr) {
ALOGE("Cannot acquire canvas from Skia.");
- return BAD_VALUE;
+ resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+ return;
}
// setup color filter if necessary
@@ -785,6 +797,18 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
const bool ctModifiesAlpha =
displayColorTransform && !displayColorTransform->isAlphaUnchanged();
+ // Find the max layer white point to determine the max luminance of the scene...
+ const float maxLayerWhitePoint = std::transform_reduce(
+ layers.cbegin(), layers.cend(), 0.f,
+ [](float left, float right) { return std::max(left, right); },
+ [&](const auto& l) { return l.whitePointNits; });
+
+ // ...and compute the dimming ratio if dimming is requested
+ const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
+ maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
+ ? maxLayerWhitePoint / display.targetLuminanceNits
+ : 1.f;
+
// Find if any layers have requested blur, we'll use that info to decide when to render to an
// offscreen buffer and when to render to the native buffer.
sk_sp<SkSurface> activeSurface(dstSurface);
@@ -798,19 +822,19 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
if (!layerHasBlur(layer, ctModifiesAlpha)) {
continue;
}
- if (layer->backgroundBlurRadius > 0 &&
- layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ if (layer.backgroundBlurRadius > 0 &&
+ layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
requiresCompositionLayer = true;
}
- for (auto region : layer->blurRegions) {
- if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ for (auto region : layer.blurRegions) {
+ if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
requiresCompositionLayer = true;
}
}
if (requiresCompositionLayer) {
activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
- blurCompositionLayer = layer;
+ blurCompositionLayer = &layer;
break;
}
}
@@ -821,34 +845,12 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
canvas->clear(SK_ColorTRANSPARENT);
initCanvas(canvas, display);
- // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
- // view is still on-screen. The clear region could be re-specified as a black color layer,
- // however.
- if (!display.clearRegion.isEmpty()) {
- ATRACE_NAME("ClearRegion");
- size_t numRects = 0;
- Rect const* rects = display.clearRegion.getArray(&numRects);
- SkIRect skRects[numRects];
- for (int i = 0; i < numRects; ++i) {
- skRects[i] =
- SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
- }
- SkRegion clearRegion;
- SkPaint paint;
- sk_sp<SkShader> shader =
- SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
- toSkColorSpace(dstDataspace));
- paint.setShader(shader);
- clearRegion.setRects(skRects, numRects);
- canvas->drawRegion(clearRegion, paint);
- }
-
for (const auto& layer : layers) {
- ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str());
+ ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
if (kPrintLayerSettings) {
std::stringstream ls;
- PrintTo(*layer, &ls);
+ PrintTo(layer, &ls);
auto debugs = ls.str();
int pos = 0;
while (pos < debugs.size()) {
@@ -858,7 +860,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
}
sk_sp<SkImage> blurInput;
- if (blurCompositionLayer == layer) {
+ if (blurCompositionLayer == &layer) {
LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
@@ -897,17 +899,17 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
// Record the name of the layer if the capture is running.
std::stringstream layerSettings;
- PrintTo(*layer, &layerSettings);
+ PrintTo(layer, &layerSettings);
// Store the LayerSettings in additional information.
- canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
+ canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(),
SkData::MakeWithCString(layerSettings.str().c_str()));
}
// Layers have a local transform that should be applied to them
- canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
+ canvas->concat(getSkM44(layer.geometry.positionTransform).asM33());
const auto [bounds, roundRectClip] =
- getBoundsAndClip(layer->geometry.boundaries, layer->geometry.roundedCornersCrop,
- layer->geometry.roundedCornersRadius);
+ getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
+ layer.geometry.roundedCornersRadius);
if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
@@ -928,20 +930,19 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
// TODO(b/182216890): Filter out empty layers earlier
if (blurRect.width() > 0 && blurRect.height() > 0) {
- if (layer->backgroundBlurRadius > 0) {
+ if (layer.backgroundBlurRadius > 0) {
ATRACE_NAME("BackgroundBlur");
- auto blurredImage =
- mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
- blurRect);
+ auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius,
+ blurInput, blurRect);
- cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+ cachedBlurs[layer.backgroundBlurRadius] = blurredImage;
- mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
+ mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f,
blurRect, blurredImage, blurInput);
}
- canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
- for (auto region : layer->blurRegions) {
+ canvas->concat(getSkM44(layer.blurRegionTransform).asM33());
+ for (auto region : layer.blurRegions) {
if (cachedBlurs[region.blurRadius] == nullptr) {
ATRACE_NAME("BlurRegion");
cachedBlurs[region.blurRadius] =
@@ -956,19 +957,18 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
}
}
- if (layer->shadow.length > 0) {
+ if (layer.shadow.length > 0) {
// This would require a new parameter/flag to SkShadowUtils::DrawShadow
- LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
+ LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with a shadow");
SkRRect shadowBounds, shadowClip;
- if (layer->geometry.boundaries == layer->shadow.boundaries) {
+ if (layer.geometry.boundaries == layer.shadow.boundaries) {
shadowBounds = bounds;
shadowClip = roundRectClip;
} else {
std::tie(shadowBounds, shadowClip) =
- getBoundsAndClip(layer->shadow.boundaries,
- layer->geometry.roundedCornersCrop,
- layer->geometry.roundedCornersRadius);
+ getBoundsAndClip(layer.shadow.boundaries, layer.geometry.roundedCornersCrop,
+ layer.geometry.roundedCornersRadius);
}
// Technically, if bounds is a rect and roundRectClip is not empty,
@@ -979,18 +979,21 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
// looks more like the intent.
const auto& rrect =
shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
- drawShadow(canvas, rrect, layer->shadow);
+ drawShadow(canvas, rrect, layer.shadow);
}
- const bool requiresLinearEffect = layer->colorTransform != mat4() ||
+ const float layerDimmingRatio = layer.whitePointNits <= 0.f
+ ? displayDimmingRatio
+ : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;
+
+ const bool requiresLinearEffect = layer.colorTransform != mat4() ||
(mUseColorManagement &&
- needsToneMapping(layer->sourceDataspace, display.outputDataspace)) ||
- (display.sdrWhitePointNits > 0.f &&
- display.sdrWhitePointNits != display.maxLuminance);
+ needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
+ !equalsWithinMargin(1.f, layerDimmingRatio, 0.001f);
// quick abort from drawing the remaining portion of the layer
- if (layer->skipContentDraw ||
- (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
+ if (layer.skipContentDraw ||
+ (layer.alpha == 0 && !requiresLinearEffect && !layer.disableBlending &&
(!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
continue;
}
@@ -1000,13 +1003,13 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
// management is a no-op.
const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
? dstDataspace
- : layer->sourceDataspace;
+ : layer.sourceDataspace;
SkPaint paint;
- if (layer->source.buffer.buffer) {
+ if (layer.source.buffer.buffer) {
ATRACE_NAME("DrawImage");
- validateInputBufferUsage(layer->source.buffer.buffer->getBuffer());
- const auto& item = layer->source.buffer;
+ validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
+ const auto& item = layer.source.buffer;
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
@@ -1025,8 +1028,8 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
// if the layer's buffer has a fence, then we must must respect the fence prior to using
// the buffer.
- if (layer->source.buffer.fence != nullptr) {
- waitFence(layer->source.buffer.fence->get());
+ if (layer.source.buffer.fence != nullptr) {
+ waitFence(layer.source.buffer.fence->get());
}
// isOpaque means we need to ignore the alpha in the image,
@@ -1070,7 +1073,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
sk_sp<SkShader> shader;
- if (layer->source.buffer.useTextureFiltering) {
+ if (layer.source.buffer.useTextureFiltering) {
shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
SkSamplingOptions(
{SkFilterMode::kLinear, SkMipmapMode::kNone}),
@@ -1085,24 +1088,39 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
toSkColorSpace(layerDataspace)));
}
- paint.setShader(createRuntimeEffectShader(shader, layer, display,
- !item.isOpaque && item.usePremultipliedAlpha,
- requiresLinearEffect));
- paint.setAlphaf(layer->alpha);
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = !item.isOpaque &&
+ item.usePremultipliedAlpha,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
+
+ // Turn on dithering when dimming beyond this threshold.
+ static constexpr float kDimmingThreshold = 0.2f;
+ if (layerDimmingRatio <= kDimmingThreshold) {
+ paint.setDither(true);
+ }
+ paint.setAlphaf(layer.alpha);
} else {
ATRACE_NAME("DrawColor");
- const auto color = layer->source.solidColor;
+ const auto color = layer.source.solidColor;
sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
.fG = color.g,
.fB = color.b,
- .fA = layer->alpha},
+ .fA = layer.alpha},
toSkColorSpace(layerDataspace));
- paint.setShader(createRuntimeEffectShader(shader, layer, display,
- /* undoPremultipliedAlpha */ false,
- requiresLinearEffect));
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = false,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
}
- if (layer->disableBlending) {
+ if (layer.disableBlending) {
paint.setBlendMode(SkBlendMode::kSrc);
}
@@ -1131,13 +1149,11 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
activeSurface->flush();
}
- if (drawFence != nullptr) {
- *drawFence = flush();
- }
+ base::unique_fd drawFence = flush();
// If flush failed or we don't support native fences, we need to force the
// gl command stream to be executed.
- bool requireSync = drawFence == nullptr || drawFence->get() < 0;
+ bool requireSync = drawFence.get() < 0;
if (requireSync) {
ATRACE_BEGIN("Submit(sync=true)");
} else {
@@ -1149,11 +1165,13 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
ALOGE("Failed to flush RenderEngine commands");
// Chances are, something illegal happened (either the caller passed
// us bad parameters, or we messed up our shader generation).
- return INVALID_OPERATION;
+ resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+ return;
}
// checkErrors();
- return NO_ERROR;
+ resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+ return;
}
inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
@@ -1164,6 +1182,73 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
}
+/**
+ * Verifies that common, simple bounds + clip combinations can be converted into
+ * a single RRect draw call returning true if possible. If true the radii parameter
+ * will be filled with the correct radii values that combined with bounds param will
+ * produce the insected roundRect. If false, the returned state of the radii param is undefined.
+ */
+static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
+ const SkRect& insetCrop, float cornerRadius,
+ SkVector radii[4]) {
+ const bool leftEqual = bounds.fLeft == crop.fLeft;
+ const bool topEqual = bounds.fTop == crop.fTop;
+ const bool rightEqual = bounds.fRight == crop.fRight;
+ const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
+ // In the event that the corners of the bounds only partially align with the crop we
+ // need to ensure that the resulting shape can still be represented as a round rect.
+ // In particular the round rect implementation will scale the value of all corner radii
+ // if the sum of the radius along any edge is greater than the length of that edge.
+ // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
+ const bool requiredWidth = bounds.width() > (cornerRadius * 2);
+ const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+ if (!requiredWidth || !requiredHeight) {
+ return false;
+ }
+
+ // Check each cropped corner to ensure that it exactly matches the crop or its corner is
+ // contained within the cropped shape and does not need rounded.
+ // compute the UpperLeft corner radius
+ if (leftEqual && topEqual) {
+ radii[0].set(cornerRadius, cornerRadius);
+ } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
+ radii[0].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the UpperRight corner radius
+ if (rightEqual && topEqual) {
+ radii[1].set(cornerRadius, cornerRadius);
+ } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fRight <= insetCrop.fRight)) {
+ radii[1].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the BottomRight corner radius
+ if (rightEqual && bottomEqual) {
+ radii[2].set(cornerRadius, cornerRadius);
+ } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
+ radii[2].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the BottomLeft corner radius
+ if (leftEqual && bottomEqual) {
+ radii[3].set(cornerRadius, cornerRadius);
+ } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
+ radii[3].set(0, 0);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
const FloatRect& cropRect,
const float cornerRadius) {
@@ -1181,66 +1266,20 @@ inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const Fl
// converting them to a single RRect draw. It is possible there are other cases
// that can be converted.
if (crop.contains(bounds)) {
- bool intersectionIsRoundRect = true;
- // check each cropped corner to ensure that it exactly matches the crop or is full
- SkVector radii[4];
-
const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
-
- const bool leftEqual = bounds.fLeft == crop.fLeft;
- const bool topEqual = bounds.fTop == crop.fTop;
- const bool rightEqual = bounds.fRight == crop.fRight;
- const bool bottomEqual = bounds.fBottom == crop.fBottom;
-
- // compute the UpperLeft corner radius
- if (leftEqual && topEqual) {
- radii[0].set(cornerRadius, cornerRadius);
- } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
- (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
- insetCrop.contains(bounds.fLeft, bounds.fTop)) {
- radii[0].set(0, 0);
- } else {
- intersectionIsRoundRect = false;
- }
- // compute the UpperRight corner radius
- if (rightEqual && topEqual) {
- radii[1].set(cornerRadius, cornerRadius);
- } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
- (topEqual && bounds.fRight <= insetCrop.fRight) ||
- insetCrop.contains(bounds.fRight, bounds.fTop)) {
- radii[1].set(0, 0);
- } else {
- intersectionIsRoundRect = false;
- }
- // compute the BottomRight corner radius
- if (rightEqual && bottomEqual) {
- radii[2].set(cornerRadius, cornerRadius);
- } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
- (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
- insetCrop.contains(bounds.fRight, bounds.fBottom)) {
- radii[2].set(0, 0);
- } else {
- intersectionIsRoundRect = false;
- }
- // compute the BottomLeft corner radius
- if (leftEqual && bottomEqual) {
- radii[3].set(cornerRadius, cornerRadius);
- } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
- (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
- insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
- radii[3].set(0, 0);
- } else {
- intersectionIsRoundRect = false;
+ if (insetCrop.contains(bounds)) {
+ return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
}
- if (intersectionIsRoundRect) {
+ SkVector radii[4];
+ if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {
SkRRect intersectionBounds;
intersectionBounds.setRectRadii(bounds, radii);
return {intersectionBounds, clip};
}
}
- // we didn't it any of our fast paths so set the clip to the cropRect
+ // we didn't hit any of our fast paths so set the clip to the cropRect
clip.setRectXY(crop, cornerRadius, cornerRadius);
}
@@ -1249,13 +1288,13 @@ inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const Fl
return {SkRRect::MakeRect(bounds), clip};
}
-inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer,
+inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings& layer,
bool colorTransformModifiesAlpha) {
- if (layer->backgroundBlurRadius > 0 || layer->blurRegions.size()) {
+ if (layer.backgroundBlurRadius > 0 || layer.blurRegions.size()) {
// return false if the content is opaque and would therefore occlude the blur
- const bool opaqueContent = !layer->source.buffer.buffer || layer->source.buffer.isOpaque;
- const bool opaqueAlpha = layer->alpha == 1.0f && !colorTransformModifiesAlpha;
- return layer->skipContentDraw || !(opaqueContent && opaqueAlpha);
+ const bool opaqueContent = !layer.source.buffer.buffer || layer.source.buffer.isOpaque;
+ const bool opaqueAlpha = layer.alpha == 1.0f && !colorTransformModifiesAlpha;
+ return layer.skipContentDraw || !(opaqueContent && opaqueAlpha);
}
return false;
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index e1162f5574..a650313648 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -54,11 +54,6 @@ public:
~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
std::future<void> primeCache() override;
- status_t drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) override;
void cleanupPostRender() override;
void cleanFramebufferCache() override{};
int getContextPriority() override;
@@ -77,6 +72,11 @@ protected:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
bool canSkipPostRenderCleanup() const override;
+ void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
private:
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
@@ -92,7 +92,7 @@ private:
inline SkRect getSkRect(const Rect& layer);
inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
const FloatRect& crop, float cornerRadius);
- inline bool layerHasBlur(const LayerSettings* layer, bool colorTransformModifiesAlpha);
+ inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
inline SkPoint3 getSkPoint3(const vec3& vector);
@@ -106,13 +106,18 @@ private:
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
const ShadowSettings& shadowSettings);
+
// If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
// Otherwise it returns the input shader.
- sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader,
- const LayerSettings* layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect);
+ struct RuntimeEffectShaderParameters {
+ sk_sp<SkShader> shader;
+ const LayerSettings& layer;
+ const DisplaySettings& display;
+ bool undoPremultipliedAlpha;
+ bool requiresLinearEffect;
+ float layerDimmingRatio;
+ };
+ sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
EGLDisplay mEGLDisplay;
EGLContext mEGLContext;
@@ -134,7 +139,8 @@ private:
// Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
GUARDED_BY(mRenderingMutex);
- std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+ std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher>
+ mRuntimeEffects;
AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
StretchShaderFactory mStretchShaderFactory;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 7cd9eca976..eb65e83324 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -44,14 +44,6 @@ public:
virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
virtual bool isProtected() const override { return false; } // mInProtectedContext; }
virtual bool supportsProtectedContent() const override { return false; };
- virtual status_t drawLayers(const DisplaySettings& /*display*/,
- const std::vector<const LayerSettings*>& /*layers*/,
- const std::shared_ptr<ExternalTexture>& /*buffer*/,
- const bool /*useFramebufferCache*/,
- base::unique_fd&& /*bufferFence*/,
- base::unique_fd* /*drawFence*/) override {
- return 0;
- };
virtual int getContextPriority() override { return 0; }
virtual void assertShadersCompiled(int numShaders) {}
virtual int reportShadersCompiled() { return 0; }
@@ -60,6 +52,14 @@ protected:
virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
bool /*isRenderable*/) override = 0;
virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0;
+
+ virtual void drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) override {
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ };
};
} // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 7c5bee9450..6746e479d2 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
#include "BlurFilter.h"
#include <SkCanvas.h>
#include <SkData.h>
@@ -32,40 +31,14 @@ namespace android {
namespace renderengine {
namespace skia {
-BlurFilter::BlurFilter() {
- SkString blurString(R"(
- uniform shader input;
- uniform float2 in_blurOffset;
- uniform float2 in_maxSizeXY;
-
- half4 main(float2 xy) {
- half4 c = sample(input, xy);
- c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
-
- return half4(c.rgb * 0.2, 1.0);
- }
- )");
-
- auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
- if (!blurEffect) {
- LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
- }
- mBlurEffect = std::move(blurEffect);
-
+static sk_sp<SkRuntimeEffect> createMixEffect() {
SkString mixString(R"(
uniform shader blurredInput;
uniform shader originalInput;
uniform float mixFactor;
half4 main(float2 xy) {
- return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
+ return half4(mix(originalInput.eval(xy), blurredInput.eval(xy), mixFactor));
}
)");
@@ -73,58 +46,12 @@ BlurFilter::BlurFilter() {
if (!mixEffect) {
LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
}
- mMixEffect = std::move(mixEffect);
+ return mixEffect;
}
-sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
- const sk_sp<SkImage> input, const SkRect& blurRect) const {
- // Kawase is an approximation of Gaussian, but it behaves differently from it.
- // A radius transformation is required for approximating them, and also to introduce
- // non-integer steps, necessary to smoothly interpolate large radii.
- float tmpRadius = (float)blurRadius / 2.0f;
- float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
- float radiusByPasses = tmpRadius / (float)numberOfPasses;
-
- // create blur surface with the bit depth and colorspace of the original surface
- SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
- std::ceil(blurRect.height() * kInputScale));
-
- const float stepX = radiusByPasses;
- const float stepY = radiusByPasses;
-
- // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
- // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
- // but instead we must do the inverse.
- SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
- blurMatrix.postScale(kInputScale, kInputScale);
-
- // start by downscaling and doing the first blur pass
- SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
- SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
- blurBuilder.child("input") =
- input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
- blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
- blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
-
- sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
-
- // And now we'll build our chain of scaled blur stages
- for (auto i = 1; i < numberOfPasses; i++) {
- const float stepScale = (float)i * kInputScale;
- blurBuilder.child("input") =
- tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
- blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
- blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
- tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
- }
-
- return tmpBlur;
-}
-
-static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) {
- // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
+static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect,
+ const float scale) {
+ // 1. Apply the blur shader matrix, which scales up the blurred surface to its real size
auto matrix = SkMatrix::Scale(scale, scale);
// 2. Since the blurred surface has the size of the layer, we align it with the
// top left corner of the layer position.
@@ -139,6 +66,14 @@ static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRec
return matrix;
}
+BlurFilter::BlurFilter(const float maxCrossFadeRadius)
+ : mMaxCrossFadeRadius(maxCrossFadeRadius),
+ mMixEffect(maxCrossFadeRadius > 0 ? createMixEffect() : nullptr) {}
+
+float BlurFilter::getMaxCrossFadeRadius() const {
+ return mMaxCrossFadeRadius;
+}
+
void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
@@ -153,7 +88,7 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
linearSampling, &blurMatrix);
- if (blurRadius < kMaxCrossFadeRadius) {
+ if (blurRadius < mMaxCrossFadeRadius) {
// For sampling Skia's API expects the inverse of what logically seems appropriate. In this
// case you might expect the matrix to simply be the canvas matrix.
SkMatrix inputMatrix;
@@ -166,7 +101,7 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
blurBuilder.child("originalInput") =
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
inputMatrix);
- blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
+ blurBuilder.uniform("mixFactor") = blurRadius / mMaxCrossFadeRadius;
paint.setShader(blurBuilder.makeShader(nullptr, true));
} else {
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 7110018367..9cddc757fc 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -27,29 +27,19 @@ namespace android {
namespace renderengine {
namespace skia {
-/**
- * This is an implementation of a Kawase blur, as described in here:
- * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
- * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
- */
class BlurFilter {
public:
// Downsample FBO to improve performance
static constexpr float kInputScale = 0.25f;
// Downsample scale factor used to improve performance
static constexpr float kInverseInputScale = 1.0f / kInputScale;
- // Maximum number of render passes
- static constexpr uint32_t kMaxPasses = 4;
- // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
- // image, up to this radius.
- static constexpr float kMaxCrossFadeRadius = 10.0f;
- explicit BlurFilter();
- virtual ~BlurFilter(){};
+ explicit BlurFilter(float maxCrossFadeRadius = 10.0f);
+ virtual ~BlurFilter(){}
// Execute blur, saving it to a texture
- sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
- const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
+ virtual sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const = 0;
/**
* Draw the blurred content (from the generate method) into the canvas.
@@ -61,13 +51,20 @@ public:
* @param blurredImage down-sampled blurred content that was produced by the generate() method
* @param input original unblurred input that is used to crossfade with the blurredImage
*/
- void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
- const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
- sk_sp<SkImage> input);
+ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+ const uint32_t blurRadius, const float blurAlpha,
+ const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+ sk_sp<SkImage> input);
+
+ float getMaxCrossFadeRadius() const;
private:
- sk_sp<SkRuntimeEffect> mBlurEffect;
- sk_sp<SkRuntimeEffect> mMixEffect;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ const float mMaxCrossFadeRadius;
+
+ // Optional blend used for crossfade only if mMaxCrossFadeRadius > 0
+ const sk_sp<SkRuntimeEffect> mMixEffect;
};
} // namespace skia
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
new file mode 100644
index 0000000000..55867a95cc
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GaussianBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkImageFilters.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// This constant approximates the scaling done in the software path's
+// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
+static const float BLUR_SIGMA_SCALE = 0.57735f;
+
+GaussianBlurFilter::GaussianBlurFilter(): BlurFilter(/* maxCrossFadeRadius= */ 0.0f) {}
+
+sk_sp<SkImage> GaussianBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect)
+ const {
+ // Create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
+ sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, scaledInfo);
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ paint.setImageFilter(SkImageFilters::Blur(
+ blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+ blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+ SkTileMode::kClamp, nullptr));
+
+ surface->getCanvas()->drawImageRect(
+ input,
+ blurRect,
+ SkRect::MakeWH(scaledInfo.width(), scaledInfo.height()),
+ SkSamplingOptions{SkFilterMode::kLinear, SkMipmapMode::kNone},
+ &paint,
+ SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ return surface->makeImageSnapshot();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.h b/libs/renderengine/skia/filters/GaussianBlurFilter.h
new file mode 100644
index 0000000000..a4febd2257
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Gaussian blur using Skia's built-in GaussianBlur filter.
+ */
+class GaussianBlurFilter: public BlurFilter {
+public:
+ explicit GaussianBlurFilter();
+ virtual ~GaussianBlurFilter(){}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
new file mode 100644
index 0000000000..bfde06fd9a
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
+ SkString blurString(R"(
+ uniform shader child;
+ uniform float in_blurOffset;
+
+ half4 main(float2 xy) {
+ half4 c = child.eval(xy);
+ c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));
+ c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));
+ c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));
+ c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));
+ return half4(c.rgb * 0.2, 1.0);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+ if (!blurEffect) {
+ LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+ }
+ mBlurEffect = std::move(blurEffect);
+}
+
+sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect)
+ const {
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ float tmpRadius = (float)blurRadius / 2.0f;
+ float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+ float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+ // create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
+
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
+ // but instead we must do the inverse.
+ SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+ blurMatrix.postScale(kInputScale, kInputScale);
+
+ // start by downscaling and doing the first blur pass
+ SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
+ blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
+
+ sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+
+ // And now we'll build our chain of scaled blur stages
+ for (auto i = 1; i < numberOfPasses; i++) {
+ blurBuilder.child("child") =
+ tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
+ blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
+ tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+ }
+
+ return tmpBlur;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.h b/libs/renderengine/skia/filters/KawaseBlurFilter.h
new file mode 100644
index 0000000000..0ac5ac8866
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class KawaseBlurFilter: public BlurFilter {
+public:
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+
+ explicit KawaseBlurFilter();
+ virtual ~KawaseBlurFilter(){}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index fc45af945b..36305aeea8 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -19,423 +19,19 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <SkString.h>
+#include <log/log.h>
+#include <shaders/shaders.h>
#include <utils/Trace.h>
-#include <optional>
-
-#include "log/log.h"
-#include "math/mat4.h"
-#include "system/graphics-base-v1.0.h"
-#include "ui/ColorSpace.h"
+#include <math/mat4.h>
namespace android {
namespace renderengine {
namespace skia {
-static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
-
- float3 EOTF(float3 color) {
- float m1 = (2610.0 / 4096.0) / 4.0;
- float m2 = (2523.0 / 4096.0) * 128.0;
- float c1 = (3424.0 / 4096.0);
- float c2 = (2413.0 / 4096.0) * 32.0;
- float c3 = (2392.0 / 4096.0) * 32.0;
-
- float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
- tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
- return pow(tmp, 1.0 / float3(m1));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float EOTF_channel(float channel) {
- const float a = 0.17883277;
- const float b = 0.28466892;
- const float c = 0.55991073;
- return channel <= 0.5 ? channel * channel / 3.0 :
- (exp((channel - c) / a) + b) / 12.0;
- }
-
- float3 EOTF(float3 color) {
- return float3(EOTF_channel(color.r), EOTF_channel(color.g),
- EOTF_channel(color.b));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- shader.append(R"(
- float3 EOTF(float3 color) {
- return color;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SRGB:
- default:
- shader.append(R"(
-
- float EOTF_sRGB(float srgb) {
- return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
- }
-
- float3 EOTF_sRGB(float3 srgb) {
- return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- float3 EOTF(float3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )");
- break;
- }
-}
-
-static void generateXYZTransforms(SkString& shader) {
- shader.append(R"(
- uniform float4x4 in_rgbToXyz;
- uniform float4x4 in_xyzToRgb;
- float3 ToXYZ(float3 rgb) {
- return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0);
- }
-
- float3 ToRGB(float3 xyz) {
- return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
- }
- )");
-}
-
-// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
-static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkString& shader) {
- switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * 10000.0;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * 1000.0 * pow(xyz.y, 0.2);
- }
- )");
- break;
- default:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_inputMaxLuminance;
- }
- )");
- break;
- }
-}
-
-static void generateToneMapInterpolation(ui::Dataspace inputDataspace,
- ui::Dataspace outputDataspace, SkString& shader) {
- switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return xyz;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
- // we'll clamp the luminance range in case we're mapping from PQ input to HLG
- // output.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return clamp(xyz, 0.0, 1000.0);
- }
- )");
- break;
- default:
- // Here we're mapping from HDR to SDR content, so interpolate using a Hermitian
- // polynomial onto the smaller luminance range.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- float maxInLumi = in_inputMaxLuminance;
- float maxOutLumi = in_displayMaxLuminance;
-
- float nits = xyz.y;
-
- // if the max input luminance is less than what we can output then
- // no tone mapping is needed as all color values will be in range.
- if (maxInLumi <= maxOutLumi) {
- return xyz;
- } else {
-
- // three control points
- const float x0 = 10.0;
- const float y0 = 17.0;
- float x1 = maxOutLumi * 0.75;
- float y1 = x1;
- float x2 = x1 + (maxInLumi - x1) / 2.0;
- float y2 = y1 + (maxOutLumi - y1) * 0.75;
-
- // horizontal distances between the last three control points
- float h12 = x2 - x1;
- float h23 = maxInLumi - x2;
- // tangents at the last three control points
- float m1 = (y2 - y1) / h12;
- float m3 = (maxOutLumi - y2) / h23;
- float m2 = (m1 + m3) / 2.0;
-
- if (nits < x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- float slope = y0 / x0;
- return xyz * slope;
- } else if (nits < x1) {
- // scale [x0, x1] to [y0, y1] linearly
- float slope = (y1 - y0) / (x1 - x0);
- nits = y0 + (nits - x0) * slope;
- } else if (nits < x2) {
- // scale [x1, x2] to [y1, y2] using Hermite interp
- float t = (nits - x1) / h12;
- nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
- (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
- } else {
- // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
- float t = (nits - x2) / h23;
- nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
- (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
- }
- }
-
- // color.y is greater than x0 and is thus non-zero
- return xyz * (nits / xyz.y);
- }
- )");
- break;
- }
- break;
- default:
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- // Map from SDR onto an HDR output buffer
- // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
- // [0, maxOutLumi] which is hard-coded to be 3000 nits.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- const float maxOutLumi = 3000.0;
-
- const float x0 = 5.0;
- const float y0 = 2.5;
- float x1 = in_displayMaxLuminance * 0.7;
- float y1 = maxOutLumi * 0.15;
- float x2 = in_displayMaxLuminance * 0.9;
- float y2 = maxOutLumi * 0.45;
- float x3 = in_displayMaxLuminance;
- float y3 = maxOutLumi;
-
- float c1 = y1 / 3.0;
- float c2 = y2 / 2.0;
- float c3 = y3 / 1.5;
-
- float nits = xyz.y;
-
- if (nits <= x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- float slope = y0 / x0;
- return xyz * slope;
- } else if (nits <= x1) {
- // scale [x0, x1] to [y0, y1] using a curve
- float t = (nits - x0) / (x1 - x0);
- nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
- } else if (nits <= x2) {
- // scale [x1, x2] to [y1, y2] using a curve
- float t = (nits - x1) / (x2 - x1);
- nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
- } else {
- // scale [x2, x3] to [y2, y3] using a curve
- float t = (nits - x2) / (x3 - x2);
- nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
- }
-
- // xyz.y is greater than x0 and is thus non-zero
- return xyz * (nits / xyz.y);
- }
- )");
- break;
- default:
- // For completeness, this is tone-mapping from SDR to SDR, where this is just a
- // no-op.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return xyz;
- }
- )");
- break;
- }
- break;
- }
-}
-
-// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
-static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, SkString& shader) {
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 10000.0;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
- }
- )");
- break;
- default:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / in_displayMaxLuminance;
- }
- )");
- break;
- }
-}
-
-static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
- SkString& shader) {
- // Input uniforms
- shader.append(R"(
- uniform float in_displayMaxLuminance;
- uniform float in_inputMaxLuminance;
- )");
-
- generateLuminanceScalesForOOTF(inputDataspace, shader);
- generateToneMapInterpolation(inputDataspace, outputDataspace, shader);
- generateLuminanceNormalizationForOOTF(outputDataspace, shader);
-
- shader.append(R"(
- float3 OOTF(float3 xyz) {
- return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
- }
- )");
-}
-
-static void generateOETF(ui::Dataspace dataspace, SkString& shader) {
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
-
- float3 OETF(float3 xyz) {
- float m1 = (2610.0 / 4096.0) / 4.0;
- float m2 = (2523.0 / 4096.0) * 128.0;
- float c1 = (3424.0 / 4096.0);
- float c2 = (2413.0 / 4096.0) * 32.0;
- float c3 = (2392.0 / 4096.0) * 32.0;
-
- float3 tmp = pow(xyz, float3(m1));
- tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
- return pow(tmp, float3(m2));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float OETF_channel(float channel) {
- const float a = 0.17883277;
- const float b = 0.28466892;
- const float c = 0.55991073;
- return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
- a * log(12.0 * channel - b) + c;
- }
-
- float3 OETF(float3 linear) {
- return float3(OETF_channel(linear.r), OETF_channel(linear.g),
- OETF_channel(linear.b));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- shader.append(R"(
- float3 OETF(float3 linear) {
- return linear;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SRGB:
- default:
- shader.append(R"(
- float OETF_sRGB(float linear) {
- return linear <= 0.0031308 ?
- linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
- }
-
- float3 OETF_sRGB(float3 linear) {
- return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float3 OETF(float3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )");
- break;
- }
-}
-
-static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
- shader.append(R"(
- uniform shader input;
- half4 main(float2 xy) {
- float4 c = float4(sample(input, xy));
- )");
- if (undoPremultipliedAlpha) {
- shader.append(R"(
- c.rgb = c.rgb / (c.a + 0.0019);
- )");
- }
- shader.append(R"(
- c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
- )");
- if (undoPremultipliedAlpha) {
- shader.append(R"(
- c.rgb = c.rgb * (c.a + 0.0019);
- )");
- }
- shader.append(R"(
- return c;
- }
- )");
-}
-static ColorSpace toColorSpace(ui::Dataspace dataspace) {
- switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
- case HAL_DATASPACE_STANDARD_BT709:
- return ColorSpace::sRGB();
- break;
- case HAL_DATASPACE_STANDARD_DCI_P3:
- return ColorSpace::DisplayP3();
- break;
- case HAL_DATASPACE_STANDARD_BT2020:
- return ColorSpace::BT2020();
- break;
- default:
- return ColorSpace::sRGB();
- break;
- }
-}
-
-sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect) {
ATRACE_CALL();
- SkString shaderString;
- generateEOTF(linearEffect.inputDataspace, shaderString);
- generateXYZTransforms(shaderString);
- generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
- generateOETF(linearEffect.outputDataspace, shaderString);
- generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ SkString shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
if (!shader) {
@@ -444,32 +40,23 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
return shader;
}
-sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
+ const shaders::LinearEffect& linearEffect,
sk_sp<SkRuntimeEffect> runtimeEffect,
const mat4& colorTransform, float maxDisplayLuminance,
float maxLuminance) {
ATRACE_CALL();
SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
- effectBuilder.child("input") = shader;
+ effectBuilder.child("child") = shader;
- if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
- effectBuilder.uniform("in_rgbToXyz") = mat4();
- effectBuilder.uniform("in_xyzToRgb") = colorTransform;
- } else {
- ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
- ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+ const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, colorTransform,
+ maxDisplayLuminance, maxLuminance);
- effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
- effectBuilder.uniform("in_xyzToRgb") =
- colorTransform * mat4(outputColorSpace.getXYZtoRGB());
+ for (const auto& uniform : uniforms) {
+ effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
}
- effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
- // If the input luminance is unknown, use display luminance (aka, no-op any luminance changes)
- // This will be the case for eg screenshots in addition to uncalibrated displays
- effectBuilder.uniform("in_inputMaxLuminance") =
- maxLuminance > 0 ? maxLuminance : maxDisplayLuminance;
return effectBuilder.makeShader(nullptr, false);
}
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 14a3b61ede..8eb6670150 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -20,6 +20,7 @@
#include <optional>
+#include <shaders/shaders.h>
#include "SkRuntimeEffect.h"
#include "SkShader.h"
#include "ui/GraphicTypes.h"
@@ -28,61 +29,7 @@ namespace android {
namespace renderengine {
namespace skia {
-/**
- * Arguments for creating an effect that applies color transformations in linear XYZ space.
- * A linear effect is decomposed into the following steps when operating on an image:
- * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
- * relative display brightness of the scene in nits for each RGB channel
- * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
- * luminance.
- * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
- * mapping to display SDR content alongside HDR content, or any number of subjective transformations
- * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
- * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
- * output RGB colors.
- *
- * For further reading, consult the recommendation in ITU-R BT.2390-4:
- * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
- *
- * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
- * intended to be the output surface. However, Skia does not support complex tone mapping such as
- * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
- * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
- * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
- * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
- * and intermediate values are interpolated.
- */
-struct LinearEffect {
- // Input dataspace of the source colors.
- const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
-
- // Working dataspace for the output surface, for conversion from linear space.
- const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
-
- // Sets whether alpha premultiplication must be undone.
- // This is required if the source colors use premultiplied alpha and is not opaque.
- const bool undoPremultipliedAlpha = false;
-};
-
-static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
- return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
- lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha;
-}
-
-struct LinearEffectHasher {
- // Inspired by art/runtime/class_linker.cc
- // Also this is what boost:hash_combine does
- static size_t HashCombine(size_t seed, size_t val) {
- return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
- }
- size_t operator()(const LinearEffect& le) const {
- size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
- result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
- return HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
- }
-};
-
-sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect);
// Generates a shader resulting from applying the a linear effect created from
// LinearEffectArgs::buildEffect to an inputShader.
@@ -93,7 +40,7 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
// * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086
// or as the max light level from the CTA 861.3 standard.
sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
- const LinearEffect& linearEffect,
+ const shaders::LinearEffect& linearEffect,
sk_sp<SkRuntimeEffect> runtimeEffect,
const mat4& colorTransform, float maxDisplayLuminance,
float maxLuminance);
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
index 4ac5c4028b..c262e3557c 100644
--- a/libs/renderengine/skia/filters/StretchShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -184,7 +184,7 @@ static const SkString stretchShader = SkString(R"(
);
coord.x = (outU - uScrollX) * viewportWidth;
coord.y = (outV - uScrollY) * viewportHeight;
- return sample(uContentTexture, coord);
+ return uContentTexture.eval(coord);
})");
const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index d0e19dd4e2..a426850350 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -23,7 +23,10 @@ package {
cc_test {
name: "librenderengine_test",
- defaults: ["skia_deps", "surfaceflinger_defaults"],
+ defaults: [
+ "skia_deps",
+ "surfaceflinger_defaults",
+ ],
test_suites: ["device-tests"],
srcs: [
"RenderEngineTest.cpp",
@@ -36,6 +39,8 @@ cc_test {
"libgmock",
"librenderengine",
"librenderengine_mocks",
+ "libshaders",
+ "libtonemap",
],
shared_libs: [
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 33e3773d50..2a25b0bfeb 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -26,7 +26,11 @@
#include <gtest/gtest.h>
#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
#include <sync/sync.h>
+#include <system/graphics-base-v1.0.h>
+#include <tonemap/tonemap.h>
+#include <ui/ColorSpace.h>
#include <ui/PixelFormat.h>
#include <chrono>
@@ -175,7 +179,7 @@ class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderE
public:
std::shared_ptr<renderengine::ExternalTexture> allocateDefaultBuffer() {
return std::make_shared<
- renderengine::
+ renderengine::impl::
ExternalTexture>(new GraphicBuffer(DEFAULT_DISPLAY_WIDTH,
DEFAULT_DISPLAY_HEIGHT,
HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -185,15 +189,16 @@ public:
GRALLOC_USAGE_HW_TEXTURE,
"output"),
*mRE,
- renderengine::ExternalTexture::Usage::READABLE |
- renderengine::ExternalTexture::Usage::WRITEABLE);
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::
+ WRITEABLE);
}
// Allocates a 1x1 buffer to fill with a solid color
std::shared_ptr<renderengine::ExternalTexture> allocateSourceBuffer(uint32_t width,
uint32_t height) {
return std::make_shared<
- renderengine::
+ renderengine::impl::
ExternalTexture>(new GraphicBuffer(width, height,
HAL_PIXEL_FORMAT_RGBA_8888, 1,
GRALLOC_USAGE_SW_READ_OFTEN |
@@ -201,8 +206,24 @@ public:
GRALLOC_USAGE_HW_TEXTURE,
"input"),
*mRE,
- renderengine::ExternalTexture::Usage::READABLE |
- renderengine::ExternalTexture::Usage::WRITEABLE);
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::
+ WRITEABLE);
+ }
+
+ std::shared_ptr<renderengine::ExternalTexture> allocateAndFillSourceBuffer(uint32_t width,
+ uint32_t height,
+ ubyte4 color) {
+ const auto buffer = allocateSourceBuffer(width, height);
+ uint8_t* pixels;
+ buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = color.r;
+ pixels[1] = color.g;
+ pixels[2] = color.b;
+ pixels[3] = color.a;
+ buffer->getBuffer()->unlock();
+ return buffer;
}
RenderEngineTest() {
@@ -282,6 +303,13 @@ public:
void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
uint8_t tolerance = 0) {
+ auto generator = [=](Point) { return ubyte4(r, g, b, a); };
+ expectBufferColor(rect, generator, tolerance);
+ }
+
+ using ColorGenerator = std::function<ubyte4(Point location)>;
+
+ void expectBufferColor(const Rect& rect, ColorGenerator generator, uint8_t tolerance = 0) {
auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
uint8_t tmp = a >= b ? a - b : b - a;
@@ -290,10 +318,10 @@ public:
return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
};
- expectBufferColor(rect, r, g, b, a, colorCompare);
+ expectBufferColor(rect, generator, colorCompare);
}
- void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ void expectBufferColor(const Rect& region, ColorGenerator generator,
std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
uint8_t* pixels;
mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -304,19 +332,22 @@ public:
const uint8_t* src = pixels +
(mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4;
for (int32_t i = 0; i < region.getWidth(); i++) {
- const uint8_t expected[4] = {r, g, b, a};
- bool equal = colorCompare(src, expected);
- EXPECT_TRUE(equal)
+ const auto location = Point(region.left + i, region.top + j);
+ const ubyte4 colors = generator(location);
+ const uint8_t expected[4] = {colors.r, colors.g, colors.b, colors.a};
+ bool colorMatches = colorCompare(src, expected);
+ EXPECT_TRUE(colorMatches)
<< GetParam()->name().c_str() << ": "
- << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
- << "expected (" << static_cast<uint32_t>(r) << ", "
- << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
- << static_cast<uint32_t>(a) << "), "
+ << "pixel @ (" << location.x << ", " << location.y << "): "
+ << "expected (" << static_cast<uint32_t>(colors.r) << ", "
+ << static_cast<uint32_t>(colors.g) << ", "
+ << static_cast<uint32_t>(colors.b) << ", "
+ << static_cast<uint32_t>(colors.a) << "), "
<< "got (" << static_cast<uint32_t>(src[0]) << ", "
<< static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2])
<< ", " << static_cast<uint32_t>(src[3]) << ")";
src += 4;
- if (!equal && ++fails >= maxFails) {
+ if (!colorMatches && ++fails >= maxFails) {
break;
}
}
@@ -328,10 +359,11 @@ public:
}
void expectAlpha(const Rect& rect, uint8_t a) {
+ auto generator = [=](Point) { return ubyte4(0, 0, 0, a); };
auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
return colorA[3] == colorB[3];
};
- expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+ expectBufferColor(rect, generator, colorCompare);
}
void expectShadowColor(const renderengine::LayerSettings& castingLayer,
@@ -417,19 +449,19 @@ public:
DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
}
- void invokeDraw(renderengine::DisplaySettings settings,
- std::vector<const renderengine::LayerSettings*> layers) {
- base::unique_fd fence;
- status_t status =
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+ void invokeDraw(const renderengine::DisplaySettings& settings,
+ const std::vector<renderengine::LayerSettings>& layers) {
+ std::future<renderengine::RenderEngineResult> result =
+ mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
- int fd = fence.release();
- if (fd >= 0) {
- sync_wait(fd, -1);
- close(fd);
- }
+ ASSERT_TRUE(result.valid());
+ auto [status, fence] = result.get();
ASSERT_EQ(NO_ERROR, status);
+ if (fence.ok()) {
+ sync_wait(fence.get(), -1);
+ }
+
if (layers.size() > 0 && mGLESRE != nullptr) {
ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
}
@@ -437,7 +469,7 @@ public:
void drawEmptyLayers() {
renderengine::DisplaySettings settings;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
invokeDraw(settings, layers);
}
@@ -490,6 +522,18 @@ public:
void fillBufferColorTransform();
template <typename SourceVariant>
+ void fillBufferWithColorTransformAndSourceDataspace(const ui::Dataspace sourceDataspace);
+
+ template <typename SourceVariant>
+ void fillBufferColorTransformAndSourceDataspace();
+
+ template <typename SourceVariant>
+ void fillBufferWithColorTransformAndOutputDataspace(const ui::Dataspace outputDataspace);
+
+ template <typename SourceVariant>
+ void fillBufferColorTransformAndOutputDataspace();
+
+ template <typename SourceVariant>
void fillBufferWithColorTransformZeroLayerAlpha();
template <typename SourceVariant>
@@ -524,10 +568,6 @@ public:
void fillGreenColorBufferThenClearRegion();
- void clearLeftRegion();
-
- void clearRegion();
-
template <typename SourceVariant>
void drawShadow(const renderengine::LayerSettings& castingLayer,
const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
@@ -634,7 +674,7 @@ void RenderEngineTest::fillBuffer(half r, half g, half b, half a) {
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -642,7 +682,7 @@ void RenderEngineTest::fillBuffer(half r, half g, half b, half a) {
SourceVariant::fillColor(layer, r, g, b, this);
layer.alpha = a;
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -678,7 +718,7 @@ void RenderEngineTest::fillRedOffsetBuffer() {
settings.physicalDisplay = offsetRect();
settings.clip = offsetRectAtZero();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -686,7 +726,7 @@ void RenderEngineTest::fillRedOffsetBuffer() {
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -713,7 +753,7 @@ void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) {
settings.clip = Rect(2, 2);
settings.orientation = orientationFlag;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layerOne;
layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -736,9 +776,9 @@ void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) {
SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
layerThree.alpha = 1.0f;
- layers.push_back(&layerOne);
- layers.push_back(&layerTwo);
- layers.push_back(&layerThree);
+ layers.push_back(layerOne);
+ layers.push_back(layerTwo);
+ layers.push_back(layerThree);
invokeDraw(settings, layers);
}
@@ -815,7 +855,7 @@ void RenderEngineTest::fillBufferWithLayerTransform() {
settings.clip = Rect(2, 2);
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -826,7 +866,7 @@ void RenderEngineTest::fillBufferWithLayerTransform() {
layer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
layer.alpha = 1.0f;
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -848,7 +888,7 @@ void RenderEngineTest::fillBufferWithColorTransform() {
settings.clip = Rect(1, 1);
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -865,7 +905,37 @@ void RenderEngineTest::fillBufferWithColorTransform() {
layer.alpha = 1.0f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformAndSourceDataspace(
+ const ui::Dataspace sourceDataspace) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.sourceDataspace = sourceDataspace;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -877,12 +947,74 @@ void RenderEngineTest::fillBufferColorTransform() {
}
template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformAndSourceDataspace() {
+ unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap;
+ dataspaceToColorMap[ui::Dataspace::V0_BT709] = {172, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::BT2020] = {172, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {172, 0, 0, 255};
+ ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>(
+ ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_2 |
+ ui::Dataspace::RANGE_FULL);
+ dataspaceToColorMap[customizedDataspace] = {172, 0, 0, 255};
+ for (const auto& [sourceDataspace, color] : dataspaceToColorMap) {
+ fillBufferWithColorTransformAndSourceDataspace<SourceVariant>(sourceDataspace);
+ expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
+ }
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformAndOutputDataspace(
+ const ui::Dataspace outputDataspace) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+ settings.outputDataspace = outputDataspace;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformAndOutputDataspace() {
+ unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap;
+ dataspaceToColorMap[ui::Dataspace::V0_BT709] = {202, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::BT2020] = {192, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {202, 0, 0, 255};
+ ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>(
+ ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_6 |
+ ui::Dataspace::RANGE_FULL);
+ dataspaceToColorMap[customizedDataspace] = {202, 0, 0, 255};
+ for (const auto& [outputDataspace, color] : dataspaceToColorMap) {
+ fillBufferWithColorTransformAndOutputDataspace<SourceVariant>(outputDataspace);
+ expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
+ }
+}
+
+template <typename SourceVariant>
void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -895,7 +1027,7 @@ void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -913,7 +1045,7 @@ void RenderEngineTest::fillRedBufferWithRoundedCorners() {
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -923,7 +1055,7 @@ void RenderEngineTest::fillRedBufferWithRoundedCorners() {
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -954,14 +1086,14 @@ void RenderEngineTest::fillBufferAndBlurBackground() {
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings backgroundLayer;
backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
backgroundLayer.alpha = 1.0f;
- layers.push_back(&backgroundLayer);
+ layers.emplace_back(backgroundLayer);
renderengine::LayerSettings leftLayer;
leftLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -969,7 +1101,7 @@ void RenderEngineTest::fillBufferAndBlurBackground() {
Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
leftLayer.alpha = 1.0f;
- layers.push_back(&leftLayer);
+ layers.emplace_back(leftLayer);
renderengine::LayerSettings blurLayer;
blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -977,7 +1109,7 @@ void RenderEngineTest::fillBufferAndBlurBackground() {
blurLayer.backgroundBlurRadius = blurRadius;
SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
blurLayer.alpha = 0;
- layers.push_back(&blurLayer);
+ layers.emplace_back(blurLayer);
invokeDraw(settings, layers);
@@ -999,14 +1131,14 @@ void RenderEngineTest::fillSmallLayerAndBlurBackground() {
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings backgroundLayer;
backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
SourceVariant::fillColor(backgroundLayer, 1.0f, 0.0f, 0.0f, this);
backgroundLayer.alpha = 1.0f;
- layers.push_back(&backgroundLayer);
+ layers.push_back(backgroundLayer);
renderengine::LayerSettings blurLayer;
blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1014,7 +1146,7 @@ void RenderEngineTest::fillSmallLayerAndBlurBackground() {
blurLayer.backgroundBlurRadius = blurRadius;
SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
blurLayer.alpha = 0;
- layers.push_back(&blurLayer);
+ layers.push_back(blurLayer);
invokeDraw(settings, layers);
@@ -1031,7 +1163,7 @@ void RenderEngineTest::overlayCorners() {
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layersFirst;
+ std::vector<renderengine::LayerSettings> layersFirst;
renderengine::LayerSettings layerOne;
layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1040,14 +1172,14 @@ void RenderEngineTest::overlayCorners() {
SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
layerOne.alpha = 0.2;
- layersFirst.push_back(&layerOne);
+ layersFirst.push_back(layerOne);
invokeDraw(settings, layersFirst);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
0, 0, 0, 0);
- std::vector<const renderengine::LayerSettings*> layersSecond;
+ std::vector<renderengine::LayerSettings> layersSecond;
renderengine::LayerSettings layerTwo;
layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layerTwo.geometry.boundaries =
@@ -1056,7 +1188,7 @@ void RenderEngineTest::overlayCorners() {
SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
layerTwo.alpha = 1.0f;
- layersSecond.push_back(&layerTwo);
+ layersSecond.push_back(layerTwo);
invokeDraw(settings, layersSecond);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
@@ -1071,7 +1203,7 @@ void RenderEngineTest::fillRedBufferTextureTransform() {
settings.clip = Rect(1, 1);
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1103,11 +1235,11 @@ void RenderEngineTest::fillRedBufferTextureTransform() {
layer.source.buffer.buffer = buf;
layer.source.buffer.textureName = texName;
// Transform coordinates to only be inside the red quadrant.
- layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1));
+ layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f));
layer.alpha = 1.0f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -1123,7 +1255,7 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() {
// Here logical space is 1x1
settings.clip = Rect(1, 1);
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
const auto buf = allocateSourceBuffer(1, 1);
@@ -1146,7 +1278,7 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() {
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -1162,7 +1294,7 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
// Here logical space is 1x1
settings.clip = Rect(1, 1);
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
const auto buf = allocateSourceBuffer(1, 1);
@@ -1185,7 +1317,7 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -1195,28 +1327,6 @@ void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
}
-void RenderEngineTest::clearLeftRegion() {
- renderengine::DisplaySettings settings;
- settings.physicalDisplay = fullscreenRect();
- // Here logical space is 4x4
- settings.clip = Rect(4, 4);
- settings.clearRegion = Region(Rect(2, 4));
- std::vector<const renderengine::LayerSettings*> layers;
- // fake layer, without bounds should not render anything
- renderengine::LayerSettings layer;
- layers.push_back(&layer);
- invokeDraw(settings, layers);
-}
-
-void RenderEngineTest::clearRegion() {
- // Reuse mBuffer
- clearLeftRegion();
- expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
- expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT),
- 0, 0, 0, 0);
-}
-
template <typename SourceVariant>
void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
const renderengine::ShadowSettings& shadow,
@@ -1226,7 +1336,7 @@ void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLaye
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
// add background layer
renderengine::LayerSettings bgLayer;
@@ -1235,7 +1345,7 @@ void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLaye
ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
backgroundColor.b / 255.0f, this);
bgLayer.alpha = backgroundColor.a / 255.0f;
- layers.push_back(&bgLayer);
+ layers.push_back(bgLayer);
// add shadow layer
renderengine::LayerSettings shadowLayer;
@@ -1243,14 +1353,14 @@ void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLaye
shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
shadowLayer.alpha = castingLayer.alpha;
shadowLayer.shadow = shadow;
- layers.push_back(&shadowLayer);
+ layers.push_back(shadowLayer);
// add layer casting the shadow
renderengine::LayerSettings layer = castingLayer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
casterColor.b / 255.0f, this);
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -1263,7 +1373,7 @@ void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
// add background layer
renderengine::LayerSettings bgLayer;
@@ -1272,7 +1382,7 @@ void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
backgroundColor.b / 255.0f, this);
bgLayer.alpha = backgroundColor.a / 255.0f;
- layers.push_back(&bgLayer);
+ layers.push_back(bgLayer);
// add shadow layer
renderengine::LayerSettings shadowLayer;
@@ -1282,7 +1392,7 @@ void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
shadowLayer.alpha = 1.0f;
ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this);
shadowLayer.shadow = shadow;
- layers.push_back(&shadowLayer);
+ layers.push_back(shadowLayer);
invokeDraw(settings, layers);
}
@@ -1307,7 +1417,8 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
settings.clip = fullscreenRect();
// 255, 255, 255, 255 is full opaque white.
- const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f);
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
// Create layer with given color.
renderengine::LayerSettings bgLayer;
bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1318,8 +1429,8 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
// Transform the red color.
bgLayer.colorTransform = mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
- std::vector<const renderengine::LayerSettings*> layers;
- layers.push_back(&bgLayer);
+ std::vector<renderengine::LayerSettings> layers;
+ layers.push_back(bgLayer);
invokeDraw(settings, layers);
@@ -1333,35 +1444,18 @@ TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layers.push_back(&layer);
- base::unique_fd fence;
- status_t status = mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
+ layers.push_back(layer);
+ std::future<renderengine::RenderEngineResult> result =
+ mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd());
+ ASSERT_TRUE(result.valid());
+ auto [status, fence] = result.get();
ASSERT_EQ(BAD_VALUE, status);
-}
-
-TEST_P(RenderEngineTest, drawLayers_nullOutputFence) {
- initializeRenderEngine();
-
- renderengine::DisplaySettings settings;
- settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- settings.physicalDisplay = fullscreenRect();
- settings.clip = fullscreenRect();
-
- std::vector<const renderengine::LayerSettings*> layers;
- renderengine::LayerSettings layer;
- layer.geometry.boundaries = fullscreenRect().toFloatRect();
- BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layer.alpha = 1.0;
- layers.push_back(&layer);
-
- status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
- ASSERT_EQ(NO_ERROR, status);
- expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+ ASSERT_FALSE(fence.ok());
}
TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
@@ -1379,15 +1473,23 @@ TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0;
- layers.push_back(&layer);
+ layers.push_back(layer);
+
+ std::future<renderengine::RenderEngineResult> result =
+ mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd());
+ ASSERT_TRUE(result.valid());
+ auto [status, fence] = result.get();
- status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
ASSERT_EQ(NO_ERROR, status);
+ if (fence.ok()) {
+ sync_wait(fence.get(), -1);
+ }
+
ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
}
@@ -1447,6 +1549,36 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
fillBufferColorTransform<ColorSourceVariant>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<ColorSourceVariant>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<ColorSourceVariant>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<ColorSourceVariant>();
@@ -1527,6 +1659,36 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource)
fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
@@ -1607,6 +1769,36 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
@@ -1647,15 +1839,11 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
fillBufferWithoutPremultiplyAlpha();
}
-TEST_P(RenderEngineTest, drawLayers_clearRegion) {
- initializeRenderEngine();
- clearRegion();
-}
-
TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
initializeRenderEngine();
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1670,8 +1858,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(1, 1);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1689,8 +1879,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1709,8 +1901,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1730,8 +1924,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1784,22 +1980,28 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0;
- layers.push_back(&layer);
-
- base::unique_fd fenceOne;
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
- base::unique_fd fenceTwo;
- mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
-
- const int fd = fenceTwo.get();
- if (fd >= 0) {
- sync_wait(fd, -1);
+ layers.push_back(layer);
+
+ std::future<renderengine::RenderEngineResult> resultOne =
+ mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+ ASSERT_TRUE(resultOne.valid());
+ auto [statusOne, fenceOne] = resultOne.get();
+ ASSERT_EQ(NO_ERROR, statusOne);
+
+ std::future<renderengine::RenderEngineResult> resultTwo =
+ mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne));
+ ASSERT_TRUE(resultTwo.valid());
+ auto [statusTwo, fenceTwo] = resultTwo.get();
+ ASSERT_EQ(NO_ERROR, statusTwo);
+ if (fenceTwo.ok()) {
+ sync_wait(fenceTwo.get(), -1);
}
+
// Only cleanup the first time.
EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
mRE->cleanupPostRender();
@@ -1814,7 +2016,7 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) {
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1825,7 +2027,7 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) {
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
redLayer.alpha = 1.0f;
- layers.push_back(&redLayer);
+ layers.push_back(redLayer);
// Green layer with 1/3 size.
renderengine::LayerSettings greenLayer;
@@ -1840,7 +2042,7 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) {
greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
greenLayer.alpha = 1.0f;
- layers.push_back(&greenLayer);
+ layers.push_back(greenLayer);
invokeDraw(settings, layers);
@@ -1863,7 +2065,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1874,7 +2076,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
redLayer.alpha = 1.0f;
- layers.push_back(&redLayer);
+ layers.push_back(redLayer);
// Green layer with 1/2 size with parent crop rect.
renderengine::LayerSettings greenLayer = redLayer;
@@ -1882,7 +2084,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2);
greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
- layers.push_back(&greenLayer);
+ layers.push_back(greenLayer);
invokeDraw(settings, layers);
@@ -1900,6 +2102,40 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
+ redLayer.geometry.roundedCornersRadius = 64;
+ redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(redLayer);
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the top corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+
+ // ensure that the entire height of the red layer was clipped by the rounded corners crop.
+ expectBufferColor(Point(0, 31), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0);
+
+ // the bottom middle should be red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();
@@ -1924,7 +2160,7 @@ TEST_P(RenderEngineTest, testClear) {
.disableBlending = true,
};
- std::vector<const renderengine::LayerSettings*> layers{&redLayer, &clearLayer};
+ std::vector<renderengine::LayerSettings> layers{redLayer, clearLayer};
invokeDraw(display, layers);
expectBufferColor(rect, 0, 0, 0, 0);
}
@@ -1972,11 +2208,138 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) {
.disableBlending = true,
};
- std::vector<const renderengine::LayerSettings*> layers{&redLayer, &greenLayer};
+ std::vector<renderengine::LayerSettings> layers{redLayer, greenLayer};
invokeDraw(display, layers);
expectBufferColor(rect, 0, 128, 0, 128);
}
+TEST_P(RenderEngineTest, testDimming) {
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+ initializeRenderEngine();
+
+ const auto displayRect = Rect(3, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = 1000.f,
+ };
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+ const auto redBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(255, 0, 0, 255));
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 200.f,
+ };
+
+ const renderengine::LayerSettings blueLayer{
+ .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = blueBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 1000.f / 51.f,
+ };
+
+ const renderengine::LayerSettings redLayer{
+ .geometry.boundaries = FloatRect(2.f, 0.f, 3.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = redBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ // When the white point is not set for a layer, just ignore it and treat it as the same
+ // as the max layer
+ .whitePointNits = -1.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer, redLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 5, 255, 1);
+ expectBufferColor(Rect(2, 0, 3, 1), 51, 0, 0, 255, 1);
+}
+
+TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
+ initializeRenderEngine();
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ const auto displayRect = Rect(2, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = -1.f,
+ };
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 200.f,
+ };
+
+ const renderengine::LayerSettings blueLayer{
+ .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = blueBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 1000.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 255, 255);
+}
+
TEST_P(RenderEngineTest, test_isOpaque) {
initializeRenderEngine();
@@ -2018,7 +2381,7 @@ TEST_P(RenderEngineTest, test_isOpaque) {
.alpha = 1.0f,
};
- std::vector<const renderengine::LayerSettings*> layers{&greenLayer};
+ std::vector<renderengine::LayerSettings> layers{greenLayer};
invokeDraw(display, layers);
if (GetParam()->useColorManagement()) {
@@ -2027,6 +2390,157 @@ TEST_P(RenderEngineTest, test_isOpaque) {
expectBufferColor(rect, 0, 255, 0, 255);
}
}
+
+double EOTF_PQ(double channel) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2);
+ tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return std::pow(tmp, 1.0 / m1);
+}
+
+vec3 EOTF_PQ(vec3 color) {
+ return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b));
+}
+
+double OETF_sRGB(double channel) {
+ return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055;
+}
+
+int sign(float in) {
+ return in >= 0.0 ? 1 : -1;
+}
+
+vec3 OETF_sRGB(vec3 linear) {
+ return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g),
+ sign(linear.b) * OETF_sRGB(linear.b));
+}
+
+TEST_P(RenderEngineTest, test_tonemapPQMatches) {
+ if (!GetParam()->useColorManagement()) {
+ return;
+ }
+
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+
+ constexpr int32_t kGreyLevels = 256;
+
+ const auto rect = Rect(0, 0, kGreyLevels, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .maxLuminance = 750.0f,
+ .outputDataspace = ui::Dataspace::DISPLAY_P3,
+ };
+
+ auto buf = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "input"),
+ *mRE,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+ ASSERT_EQ(0, buf->getBuffer()->initCheck());
+
+ {
+ uint8_t* pixels;
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ uint8_t color = 0;
+ for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) {
+ uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4);
+ for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) {
+ dest[0] = color;
+ dest[1] = color;
+ dest[2] = color;
+ dest[3] = 255;
+ color++;
+ dest += 4;
+ }
+ }
+ buf->getBuffer()->unlock();
+ }
+
+ mBuffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "output"),
+ *mRE,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+ ASSERT_EQ(0, mBuffer->getBuffer()->initCheck());
+
+ const renderengine::LayerSettings layer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = std::move(buf),
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_ST2084 |
+ HAL_DATASPACE_RANGE_FULL),
+ };
+
+ std::vector<renderengine::LayerSettings> layers{layer};
+ invokeDraw(display, layers);
+
+ ColorSpace displayP3 = ColorSpace::DisplayP3();
+ ColorSpace bt2020 = ColorSpace::BT2020();
+
+ tonemap::Metadata metadata{.displayMaxLuminance = 750.0f};
+
+ auto generator = [=](Point location) {
+ const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1);
+ const vec3 rgb = vec3(normColor, normColor, normColor);
+
+ const vec3 linearRGB = EOTF_PQ(rgb);
+
+ static constexpr float kMaxPQLuminance = 10000.f;
+ const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance;
+ const double gain =
+ tonemap::getToneMapper()
+ ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common::
+ Dataspace>(
+ HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_ST2084 |
+ HAL_DATASPACE_RANGE_FULL),
+ static_cast<aidl::android::hardware::graphics::common::
+ Dataspace>(
+ ui::Dataspace::DISPLAY_P3),
+ linearRGB * 10000.0, xyz, metadata);
+ const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance;
+
+ const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255;
+ return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g),
+ static_cast<uint8_t>(targetRGB.b), 255);
+ };
+
+ expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
+}
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 830f4630e5..96851892b4 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -17,6 +17,7 @@
#include <cutils/properties.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <renderengine/impl/ExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
#include "../threaded/RenderEngineThreaded.h"
@@ -169,25 +170,33 @@ TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsTrue) {
status_t result = mThreadedRE->supportsBackgroundBlur();
ASSERT_EQ(true, result);
}
+
TEST_F(RenderEngineThreadedTest, drawLayers) {
renderengine::DisplaySettings settings;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
- renderengine::ExternalTexture::Usage::READABLE |
- renderengine::ExternalTexture::Usage::WRITEABLE);
- base::unique_fd bufferFence;
- base::unique_fd drawFence;
+ renderengine::impl::
+ ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
- EXPECT_CALL(*mRenderEngine, drawLayers)
- .WillOnce([](const renderengine::DisplaySettings&,
- const std::vector<const renderengine::LayerSettings*>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> status_t { return NO_ERROR; });
+ base::unique_fd bufferFence;
- status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
- std::move(bufferFence), &drawFence);
- ASSERT_EQ(NO_ERROR, result);
+ EXPECT_CALL(*mRenderEngine, drawLayersInternal)
+ .WillOnce([&](const std::shared_ptr<std::promise<renderengine::RenderEngineResult>>&&
+ resultPromise,
+ const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> void {
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ });
+
+ std::future<renderengine::RenderEngineResult> result =
+ mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+ ASSERT_TRUE(result.valid());
+ auto [status, _] = result.get();
+ ASSERT_EQ(NO_ERROR, status);
}
} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 8e666d5733..a7176d3279 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -178,6 +178,10 @@ void RenderEngineThreaded::dump(std::string& result) {
void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
ATRACE_CALL();
+ // This is a no-op in SkiaRenderEngine.
+ if (getRenderEngineType() != RenderEngineType::THREADED) {
+ return;
+ }
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
@@ -194,6 +198,10 @@ void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
ATRACE_CALL();
+ // This is a no-op in SkiaRenderEngine.
+ if (getRenderEngineType() != RenderEngineType::THREADED) {
+ return;
+ }
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
@@ -292,7 +300,7 @@ void RenderEngineThreaded::cleanupPostRender() {
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+ ATRACE_NAME("REThreaded::cleanupPostRender");
instance.cleanupPostRender();
});
}
@@ -304,27 +312,34 @@ bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
return mRenderEngine->canSkipPostRenderCleanup();
}
-status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache,
- base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) {
+void RenderEngineThreaded::drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) {
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ return;
+}
+
+std::future<RenderEngineResult> RenderEngineThreaded::drawLayers(
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) {
ATRACE_CALL();
- std::promise<status_t> resultPromise;
- std::future<status_t> resultFuture = resultPromise.get_future();
+ const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>();
+ std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
+ int fd = bufferFence.release();
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
- &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+ mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache,
+ fd](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::drawLayers");
- status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
- std::move(bufferFence), drawFence);
- resultPromise.set_value(status);
+ instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
+ useFramebufferCache, base::unique_fd(fd));
});
}
mCondition.notify_one();
- return resultFuture.get();
+ return resultFuture;
}
void RenderEngineThreaded::cleanFramebufferCache() {
@@ -374,6 +389,20 @@ void RenderEngineThreaded::onActiveDisplaySizeChanged(ui::Size size) {
mCondition.notify_one();
}
+std::optional<pid_t> RenderEngineThreaded::getRenderEngineTid() const {
+ std::promise<pid_t> tidPromise;
+ std::future<pid_t> tidFuture = tidPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&tidPromise](renderengine::RenderEngine& instance) {
+ tidPromise.set_value(gettid());
+ });
+ }
+
+ mCondition.notify_one();
+ return std::make_optional(tidFuture.get());
+}
+
} // namespace threaded
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index b197df7e0f..1ba72ddf81 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -56,21 +56,27 @@ public:
void useProtectedContext(bool useProtectedContext) override;
void cleanupPostRender() override;
- status_t drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) override;
+ std::future<RenderEngineResult> drawLayers(const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) override;
void cleanFramebufferCache() override;
int getContextPriority() override;
bool supportsBackgroundBlur() override;
void onActiveDisplaySizeChanged(ui::Size size) override;
+ std::optional<pid_t> getRenderEngineTid() const override;
protected:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
bool canSkipPostRenderCleanup() const override;
+ void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 0a49008584..da88e8541a 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -276,6 +276,10 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi
mStringType = SENSOR_STRING_TYPE_HINGE_ANGLE;
mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
break;
+ case SENSOR_TYPE_HEAD_TRACKER:
+ mStringType = SENSOR_STRING_TYPE_HEAD_TRACKER;
+ mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+ break;
default:
// Only pipe the stringType, requiredPermission and flags for custom sensors.
if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
@@ -468,7 +472,15 @@ const Sensor::uuid_t& Sensor::getUuid() const {
}
void Sensor::setId(int32_t id) {
- mUuid.i64[0] = id;
+ mId = id;
+}
+
+int32_t Sensor::getId() const {
+ return mId;
+}
+
+void Sensor::anonymizeUuid() {
+ mUuid.i64[0] = mId;
mUuid.i64[1] = 0;
}
@@ -485,17 +497,14 @@ void Sensor::capHighestDirectReportRateLevel(int32_t cappedRateLevel) {
}
}
-int32_t Sensor::getId() const {
- return int32_t(mUuid.i64[0]);
-}
-
size_t Sensor::getFlattenedSize() const {
size_t fixedSize =
sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) +
sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) +
sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) +
sizeof(mFifoMaxEventCount) + sizeof(mRequiredPermissionRuntime) +
- sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid);
+ sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) +
+ sizeof(mUuid) + sizeof(mId);
size_t variableSize =
sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) +
@@ -529,18 +538,8 @@ status_t Sensor::flatten(void* buffer, size_t size) const {
FlattenableUtils::write(buffer, size, mRequiredAppOp);
FlattenableUtils::write(buffer, size, mMaxDelay);
FlattenableUtils::write(buffer, size, mFlags);
- if (mUuid.i64[1] != 0) {
- // We should never hit this case with our current API, but we
- // could via a careless API change. If that happens,
- // this code will keep us from leaking our UUID (while probably
- // breaking dynamic sensors). See b/29547335.
- ALOGW("Sensor with UUID being flattened; sending 0. Expect "
- "bad dynamic sensor behavior");
- uuid_t tmpUuid; // default constructor makes this 0.
- FlattenableUtils::write(buffer, size, tmpUuid);
- } else {
- FlattenableUtils::write(buffer, size, mUuid);
- }
+ FlattenableUtils::write(buffer, size, mUuid);
+ FlattenableUtils::write(buffer, size, mId);
return NO_ERROR;
}
@@ -580,7 +579,7 @@ status_t Sensor::unflatten(void const* buffer, size_t size) {
size_t fixedSize2 =
sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) +
- sizeof(mFlags) + sizeof(mUuid);
+ sizeof(mFlags) + sizeof(mUuid) + sizeof(mId);
if (size < fixedSize2) {
return NO_MEMORY;
}
@@ -590,6 +589,7 @@ status_t Sensor::unflatten(void const* buffer, size_t size) {
FlattenableUtils::read(buffer, size, mMaxDelay);
FlattenableUtils::read(buffer, size, mFlags);
FlattenableUtils::read(buffer, size, mUuid);
+ FlattenableUtils::read(buffer, size, mId);
return NO_ERROR;
}
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h
index 374b68fab5..bae8a1380b 100644
--- a/libs/sensor/include/sensor/Sensor.h
+++ b/libs/sensor/include/sensor/Sensor.h
@@ -96,11 +96,8 @@ public:
bool isDirectChannelTypeSupported(int32_t sharedMemType) const;
int32_t getReportingMode() const;
- // Note that after setId() has been called, getUuid() no longer
- // returns the UUID.
- // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
- // make sure setId() doesn't change the UuidIndex.
const uuid_t& getUuid() const;
+ void anonymizeUuid();
int32_t getId() const;
void setId(int32_t id);
@@ -132,10 +129,8 @@ private:
int32_t mRequiredAppOp;
int32_t mMaxDelay;
uint32_t mFlags;
- // TODO(b/29547335): Get rid of this field and replace with an index.
- // The index will be into a separate global vector of UUIDs.
- // Also add an mId field (and change flatten/unflatten appropriately).
uuid_t mUuid;
+ int32_t mId;
static void flattenString8(void*& buffer, size_t& size, const String8& string8);
static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
};
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
new file mode 100644
index 0000000000..390b22821e
--- /dev/null
+++ b/libs/shaders/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+ name: "libshaders",
+
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ "android.hardware.graphics.common@1.2",
+ ],
+
+ static_libs: [
+ "libmath",
+ "libtonemap",
+ "libui-types",
+ ],
+
+ srcs: [
+ "shaders.cpp",
+ ],
+}
diff --git a/libs/shaders/OWNERS b/libs/shaders/OWNERS
new file mode 100644
index 0000000000..6d91da3bd2
--- /dev/null
+++ b/libs/shaders/OWNERS
@@ -0,0 +1,4 @@
+alecmouri@google.com
+jreck@google.com
+sallyqi@google.com
+scroggo@google.com \ No newline at end of file
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
new file mode 100644
index 0000000000..712a27a3eb
--- /dev/null
+++ b/libs/shaders/include/shaders/shaders.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/mat4.h>
+#include <tonemap/tonemap.h>
+#include <ui/GraphicTypes.h>
+#include <cstddef>
+
+namespace android::shaders {
+
+/**
+ * Arguments for creating an effect that applies color transformations in linear XYZ space.
+ * A linear effect is decomposed into the following steps when operating on an image:
+ * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
+ * relative display brightness of the scene in nits for each RGB channel
+ * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
+ * luminance.
+ * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
+ * mapping to display SDR content alongside HDR content, or any number of subjective transformations
+ * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
+ * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
+ * output RGB colors.
+ *
+ * For further reading, consult the recommendation in ITU-R BT.2390-4:
+ * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
+ *
+ * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
+ * intended to be the output surface. However, Skia does not support complex tone mapping such as
+ * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
+ * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
+ * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
+ * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
+ * and intermediate values are interpolated.
+ */
+struct LinearEffect {
+ // Input dataspace of the source colors.
+ const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
+
+ // Working dataspace for the output surface, for conversion from linear space.
+ const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
+
+ // Sets whether alpha premultiplication must be undone.
+ // This is required if the source colors use premultiplied alpha and is not opaque.
+ const bool undoPremultipliedAlpha = false;
+
+ // "Fake" dataspace of the source colors. This is used for applying an EOTF to compute linear
+ // RGB. This is used when Skia is expected to color manage the input image based on the
+ // dataspace of the provided source image and destination surface. SkRuntimeEffects use the
+ // destination color space as the working color space. RenderEngine deliberately sets the color
+ // space for input images and destination surfaces to be the same whenever LinearEffects are
+ // expected to be used so that color-management is controlled by RenderEngine, but other users
+ // of a LinearEffect may not be able to control the color space of the images and surfaces. So
+ // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
+ // dataspace for correct conversion to linear colors.
+ ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+};
+
+static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
+ return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
+ lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha &&
+ lhs.fakeInputDataspace == rhs.fakeInputDataspace;
+}
+
+struct LinearEffectHasher {
+ // Inspired by art/runtime/class_linker.cc
+ // Also this is what boost:hash_combine does
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
+ size_t operator()(const LinearEffect& le) const {
+ size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
+ result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
+ result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
+ return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeInputDataspace));
+ }
+};
+
+// Generates a shader string that applies color transforms in linear space.
+// Typical use-cases supported:
+// 1. Apply tone-mapping
+// 2. Apply color transform matrices in linear space
+std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
+
+// Generates a list of uniforms to set on the LinearEffect shader above.
+std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
+ const mat4& colorTransform,
+ float maxDisplayLuminance,
+ float maxLuminance);
+
+} // namespace android::shaders
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
new file mode 100644
index 0000000000..6019c4ac28
--- /dev/null
+++ b/libs/shaders/shaders.cpp
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shaders/shaders.h>
+
+#include <tonemap/tonemap.h>
+
+#include <optional>
+
+#include <math/mat4.h>
+#include <system/graphics-base-v1.0.h>
+#include <ui/ColorSpace.h>
+
+namespace android::shaders {
+
+static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(
+ ui::Dataspace dataspace) {
+ return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
+}
+
+static void generateEOTF(ui::Dataspace dataspace, std::string& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 EOTF(float3 color) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
+ tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return pow(tmp, 1.0 / float3(m1));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float EOTF_channel(float channel) {
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 0.5 ? channel * channel / 3.0 :
+ (exp((channel - c) / a) + b) / 12.0;
+ }
+
+ float3 EOTF(float3 color) {
+ return float3(EOTF_channel(color.r), EOTF_channel(color.g),
+ EOTF_channel(color.b));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ shader.append(R"(
+ float3 EOTF(float3 color) {
+ return color;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 0.45);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.2);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.6);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.8);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ default:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateXYZTransforms(std::string& shader) {
+ shader.append(R"(
+ uniform float4x4 in_rgbToXyz;
+ uniform float4x4 in_xyzToRgb;
+ float3 ToXYZ(float3 rgb) {
+ return (in_rgbToXyz * float4(rgb, 1.0)).rgb;
+ }
+
+ float3 ToRGB(float3 xyz) {
+ return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+ }
+ )");
+}
+
+// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
+static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace,
+ ui::Dataspace outputDataspace, std::string& shader) {
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 10000.0;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 1000.0 * pow(xyz.y, 0.2);
+ }
+ )");
+ break;
+ default:
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // SDR -> HDR tonemap
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_libtonemap_inputMaxLuminance;
+ }
+ )");
+ break;
+ default:
+ // Input and output are both SDR, so no tone-mapping is expected so
+ // no-op the luminance normalization.
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_libtonemap_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+ }
+}
+
+// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
+ std::string& shader) {
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 10000.0;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
+ }
+ )");
+ break;
+ default:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / in_libtonemap_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+ std::string& shader) {
+ shader.append(tonemap::getToneMapper()
+ ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
+ toAidlDataspace(outputDataspace))
+ .c_str());
+
+ generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
+ generateLuminanceNormalizationForOOTF(outputDataspace, shader);
+
+ shader.append(R"(
+ float3 OOTF(float3 linearRGB, float3 xyz) {
+ float3 scaledLinearRGB = ScaleLuminance(linearRGB);
+ float3 scaledXYZ = ScaleLuminance(xyz);
+
+ float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ);
+
+ return NormalizeLuminance(scaledXYZ * gain);
+ }
+ )");
+}
+
+static void generateOETF(ui::Dataspace dataspace, std::string& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 OETF(float3 xyz) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(xyz, float3(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, float3(m2));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float OETF_channel(float channel) {
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+ a * log(12.0 * channel - b) + c;
+ }
+
+ float3 OETF(float3 linear) {
+ return float3(OETF_channel(linear.r), OETF_channel(linear.g),
+ OETF_channel(linear.b));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ shader.append(R"(
+ float3 OETF(float3 linear) {
+ return linear;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return linear <= 0.018 ?
+ linear * 4.50 : (pow(linear, 0.45) * 1.099) - 0.099;
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.2));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.6));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.8));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ default:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return linear <= 0.0031308 ?
+ linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
+ shader.append(R"(
+ uniform shader child;
+ half4 main(float2 xy) {
+ float4 c = float4(child.eval(xy));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb / (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ float3 linearRGB = EOTF(c.rgb);
+ float3 xyz = ToXYZ(linearRGB);
+ c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz)));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb * (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ return c;
+ }
+ )");
+}
+
+// please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return ColorSpace::sRGB();
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return ColorSpace::DisplayP3();
+ case HAL_DATASPACE_STANDARD_BT2020:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ return ColorSpace::BT2020();
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ return ColorSpace::AdobeRGB();
+ // TODO(b/208290320): BT601 format and variants return different primaries
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ // TODO(b/208290329): BT407M format returns different primaries
+ case HAL_DATASPACE_STANDARD_BT470M:
+ // TODO(b/208290904): FILM format returns different primaries
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ default:
+ return ColorSpace::sRGB();
+ }
+}
+
+std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
+ std::string shaderString;
+ generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
+ ? linearEffect.inputDataspace
+ : linearEffect.fakeInputDataspace,
+ shaderString);
+ generateXYZTransforms(shaderString);
+ generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
+ generateOETF(linearEffect.outputDataspace, shaderString);
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ return shaderString;
+}
+
+template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
+std::vector<uint8_t> buildUniformValue(T value) {
+ std::vector<uint8_t> result;
+ result.resize(sizeof(value));
+ std::memcpy(result.data(), &value, sizeof(value));
+ return result;
+}
+
+// Generates a list of uniforms to set on the LinearEffect shader above.
+std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
+ const mat4& colorTransform,
+ float maxDisplayLuminance,
+ float maxLuminance) {
+ std::vector<tonemap::ShaderUniform> uniforms;
+ if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
+ uniforms.push_back({.name = "in_rgbToXyz", .value = buildUniformValue<mat4>(mat4())});
+ uniforms.push_back(
+ {.name = "in_xyzToRgb", .value = buildUniformValue<mat4>(colorTransform)});
+ } else {
+ ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+ ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+ uniforms.push_back({.name = "in_rgbToXyz",
+ .value = buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ()))});
+ uniforms.push_back({.name = "in_xyzToRgb",
+ .value = buildUniformValue<mat4>(
+ colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
+ }
+
+ tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
+ // If the input luminance is unknown, use display luminance (aka,
+ // no-op any luminance changes)
+ // This will be the case for eg screenshots in addition to
+ // uncalibrated displays
+ .contentMaxLuminance =
+ maxLuminance > 0 ? maxLuminance : maxDisplayLuminance};
+
+ for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) {
+ uniforms.push_back(uniform);
+ }
+
+ return uniforms;
+}
+
+} // namespace android::shaders \ No newline at end of file
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
new file mode 100644
index 0000000000..5360fe2b07
--- /dev/null
+++ b/libs/tonemap/Android.bp
@@ -0,0 +1,43 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+ name: "libtonemap",
+ vendor_available: true,
+
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libmath",
+ ],
+
+ srcs: [
+ "tonemap.cpp",
+ ],
+}
diff --git a/libs/tonemap/OWNERS b/libs/tonemap/OWNERS
new file mode 100644
index 0000000000..6d91da3bd2
--- /dev/null
+++ b/libs/tonemap/OWNERS
@@ -0,0 +1,4 @@
+alecmouri@google.com
+jreck@google.com
+sallyqi@google.com
+scroggo@google.com \ No newline at end of file
diff --git a/libs/tonemap/TEST_MAPPING b/libs/tonemap/TEST_MAPPING
new file mode 100644
index 0000000000..00f83baaa9
--- /dev/null
+++ b/libs/tonemap/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "librenderengine_test"
+ },
+ {
+ "name": "libtonemap_test"
+ }
+ ]
+}
diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h
new file mode 100644
index 0000000000..bd7b72d186
--- /dev/null
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <math/vec3.h>
+
+#include <string>
+#include <vector>
+
+namespace android::tonemap {
+
+// Describes a shader uniform
+// The shader uniform is intended to be passed into a SkRuntimeShaderBuilder, i.e.:
+//
+// SkRuntimeShaderBuilder builder;
+// builder.uniform(<uniform name>).set(<uniform value>.data(), <uniform value>.size());
+struct ShaderUniform {
+ // The name of the uniform, used for binding into a shader.
+ // The shader must contain a uniform whose name matches this.
+ std::string name;
+
+ // The value for the uniform, which should be bound to the uniform identified by <name>
+ std::vector<uint8_t> value;
+};
+
+// Describes metadata which may be used for constructing the shader uniforms.
+// This metadata should not be used for manipulating the source code of the shader program directly,
+// as otherwise caching by other system of these shaders may break.
+struct Metadata {
+ float displayMaxLuminance = 0.0;
+ float contentMaxLuminance = 0.0;
+};
+
+class ToneMapper {
+public:
+ virtual ~ToneMapper() {}
+ // Constructs a tonemap shader whose shader language is SkSL, which tonemaps from an
+ // input whose dataspace is described by sourceDataspace, to an output whose dataspace
+ // is described by destinationDataspace
+ //
+ // The returned shader string *must* contain a function with the following signature:
+ // float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz);
+ //
+ // The arguments are:
+ // * linearRGB is the absolute nits of the RGB pixels in linear space
+ // * xyz is linearRGB converted into XYZ
+ //
+ // libtonemap_LookupTonemapGain() returns a float representing the amount by which to scale the
+ // absolute nits of the pixels. This function may be plugged into any existing SkSL shader, and
+ // is expected to look something like this:
+ //
+ // vec3 rgb = ...;
+ // // apply the EOTF based on the incoming dataspace to convert to linear nits.
+ // vec3 linearRGB = applyEOTF(rgb);
+ // // apply a RGB->XYZ matrix float3
+ // vec3 xyz = toXYZ(linearRGB);
+ // // Scale the luminance based on the content standard
+ // vec3 absoluteRGB = ScaleLuminance(linearRGB);
+ // vec3 absoluteXYZ = ScaleLuminance(xyz);
+ // float gain = libtonemap_LookupTonemapGain(absoluteRGB, absoluteXYZ);
+ // // Normalize the luminance back down to a [0, 1] range
+ // xyz = NormalizeLuminance(absoluteXYZ * gain);
+ // // apply a XYZ->RGB matrix and apply the output OETf.
+ // vec3 finalColor = applyOETF(ToRGB(xyz));
+ // ...
+ //
+ // Helper methods in this shader should be prefixed with "libtonemap_". Accordingly, libraries
+ // which consume this shader must *not* contain any methods prefixed with "libtonemap_" to
+ // guarantee that there are no conflicts in name resolution.
+ virtual std::string generateTonemapGainShaderSkSL(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) = 0;
+
+ // Constructs uniform descriptions that correspond to those that are generated for the tonemap
+ // shader. Uniforms must be prefixed with "in_libtonemap_". Libraries which consume this shader
+ // must not bind any new uniforms that begin with this prefix.
+ //
+ // Downstream shaders may assume the existence of the uniform in_libtonemap_displayMaxLuminance
+ // and in_libtonemap_inputMaxLuminance, in order to assist with scaling and normalizing
+ // luminance as described in the documentation for generateTonemapGainShaderSkSL(). That is,
+ // shaders plugging in a tone-mapping shader returned by generateTonemapGainShaderSkSL() may
+ // assume that there are predefined floats in_libtonemap_displayMaxLuminance and
+ // in_libtonemap_inputMaxLuminance inside of the body of the tone-mapping shader.
+ virtual std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) = 0;
+
+ // CPU implementation of the tonemapping gain. This must match the GPU implementation returned
+ // by generateTonemapGainShaderSKSL() above, with some epsilon difference to account for
+ // differences in hardware precision.
+ //
+ // The gain is computed assuming an input described by sourceDataspace, tonemapped to an output
+ // described by destinationDataspace. To compute the gain, the input colors are provided by
+ // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also
+ // provided. Metadata is also provided for helping to compute the tonemapping curve.
+ virtual double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0;
+};
+
+// Retrieves a tonemapper instance.
+// This instance is globally constructed.
+ToneMapper* getToneMapper();
+
+} // namespace android::tonemap \ No newline at end of file
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
new file mode 100644
index 0000000000..f46f3fa27d
--- /dev/null
+++ b/libs/tonemap/tests/Android.bp
@@ -0,0 +1,39 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "libtonemap_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "tonemap_test.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ ],
+ static_libs: [
+ "libmath",
+ "libgmock",
+ "libgtest",
+ "libtonemap",
+ ],
+}
diff --git a/libs/tonemap/tests/tonemap_test.cpp b/libs/tonemap/tests/tonemap_test.cpp
new file mode 100644
index 0000000000..7a7958f58f
--- /dev/null
+++ b/libs/tonemap/tests/tonemap_test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <tonemap/tonemap.h>
+#include <cmath>
+
+namespace android {
+
+using testing::HasSubstr;
+
+struct TonemapTest : public ::testing::Test {};
+
+TEST_F(TonemapTest, generateShaderSkSLUniforms_containsDefaultUniforms) {
+ static const constexpr float kDisplayMaxLuminance = 1.f;
+ static const constexpr float kContentMaxLuminance = 2.f;
+ tonemap::Metadata metadata{.displayMaxLuminance = kDisplayMaxLuminance,
+ .contentMaxLuminance = kContentMaxLuminance};
+ const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata);
+
+ ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_displayMaxLuminance";
+ }));
+ ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_inputMaxLuminance";
+ }));
+
+ // Smoke check that metadata values are "real", specifically that they're non-zero and actually
+ // numbers. This is to help avoid shaders using these uniforms from dividing by zero or other
+ // catastrophic errors.
+ const auto& displayLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_displayMaxLuminance";
+ })->value;
+
+ float displayLumFloat = 0.f;
+ std::memcpy(&displayLumFloat, displayLum.data(), displayLum.size());
+ EXPECT_FALSE(std::isnan(displayLumFloat));
+ EXPECT_GT(displayLumFloat, 0);
+
+ const auto& contentLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_inputMaxLuminance";
+ })->value;
+
+ float contentLumFloat = 0.f;
+ std::memcpy(&contentLumFloat, contentLum.data(), contentLum.size());
+ EXPECT_FALSE(std::isnan(contentLumFloat));
+ EXPECT_GT(contentLumFloat, 0);
+}
+
+TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) {
+ const auto shader =
+ tonemap::getToneMapper()
+ ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common::
+ Dataspace::BT2020_ITU_PQ,
+ aidl::android::hardware::graphics::common::
+ Dataspace::DISPLAY_P3);
+
+ // Other tests such as librenderengine_test will plug in the shader to check compilation.
+ EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)"));
+}
+
+} // namespace android
diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp
new file mode 100644
index 0000000000..c2372fe828
--- /dev/null
+++ b/libs/tonemap/tonemap.cpp
@@ -0,0 +1,666 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <tonemap/tonemap.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <mutex>
+#include <type_traits>
+
+namespace android::tonemap {
+
+namespace {
+
+// Flag containing the variant of tone map algorithm to use.
+enum class ToneMapAlgorithm {
+ AndroidO, // Default algorithm in place since Android O,
+ Android13, // Algorithm used in Android 13.
+};
+
+static const constexpr auto kToneMapAlgorithm = ToneMapAlgorithm::Android13;
+
+static const constexpr auto kTransferMask =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_MASK);
+static const constexpr auto kTransferST2084 =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_ST2084);
+static const constexpr auto kTransferHLG =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_HLG);
+
+template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
+std::vector<uint8_t> buildUniformValue(T value) {
+ std::vector<uint8_t> result;
+ result.resize(sizeof(value));
+ std::memcpy(result.data(), &value, sizeof(value));
+ return result;
+}
+
+class ToneMapperO : public ToneMapper {
+public:
+ std::string generateTonemapGainShaderSkSL(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ std::string program;
+ // Define required uniforms
+ program.append(R"(
+ uniform float in_libtonemap_displayMaxLuminance;
+ uniform float in_libtonemap_inputMaxLuminance;
+ )");
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return xyz.y;
+ }
+ )");
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return clamp(xyz.y, 0.0, 1000.0);
+ }
+ )");
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ float maxInLumi = in_libtonemap_inputMaxLuminance;
+ float maxOutLumi = in_libtonemap_displayMaxLuminance;
+
+ float nits = xyz.y;
+
+ // if the max input luminance is less than what we can
+ // output then no tone mapping is needed as all color
+ // values will be in range.
+ if (maxInLumi <= maxOutLumi) {
+ return xyz.y;
+ } else {
+
+ // three control points
+ const float x0 = 10.0;
+ const float y0 = 17.0;
+ float x1 = maxOutLumi * 0.75;
+ float y1 = x1;
+ float x2 = x1 + (maxInLumi - x1) / 2.0;
+ float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+ // horizontal distances between the last three
+ // control points
+ float h12 = x2 - x1;
+ float h23 = maxInLumi - x2;
+ // tangents at the last three control points
+ float m1 = (y2 - y1) / h12;
+ float m3 = (maxOutLumi - y2) / h23;
+ float m2 = (m1 + m3) / 2.0;
+
+ if (nits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return nits * slope;
+ } else if (nits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ float slope = (y1 - y0) / (x1 - x0);
+ nits = y0 + (nits - x0) * slope;
+ } else if (nits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ float t = (nits - x1) / h12;
+ nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) *
+ (1.0 - t) * (1.0 - t) +
+ (y2 * (3.0 - 2.0 * t) +
+ h12 * m2 * (t - 1.0)) * t * t;
+ } else {
+ // scale [x2, maxInLumi] to [y2, maxOutLumi] using
+ // Hermite interp
+ float t = (nits - x2) / h23;
+ nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) *
+ (1.0 - t) * (1.0 - t) + (maxOutLumi *
+ (3.0 - 2.0 * t) + h23 * m3 *
+ (t - 1.0)) * t * t;
+ }
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ }
+ break;
+ default:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ // Map from SDR onto an HDR output buffer
+ // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
+ // [0, maxOutLumi] which is hard-coded to be 3000 nits.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ const float maxOutLumi = 3000.0;
+
+ const float x0 = 5.0;
+ const float y0 = 2.5;
+ float x1 = in_libtonemap_displayMaxLuminance * 0.7;
+ float y1 = maxOutLumi * 0.15;
+ float x2 = in_libtonemap_displayMaxLuminance * 0.9;
+ float y2 = maxOutLumi * 0.45;
+ float x3 = in_libtonemap_displayMaxLuminance;
+ float y3 = maxOutLumi;
+
+ float c1 = y1 / 3.0;
+ float c2 = y2 / 2.0;
+ float c3 = y3 / 1.5;
+
+ float nits = xyz.y;
+
+ if (nits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return nits * slope;
+ } else if (nits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ float t = (nits - x0) / (x1 - x0);
+ nits = (1.0 - t) * (1.0 - t) * y0 +
+ 2.0 * (1.0 - t) * t * c1 + t * t * y1;
+ } else if (nits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ float t = (nits - x1) / (x2 - x1);
+ nits = (1.0 - t) * (1.0 - t) * y1 +
+ 2.0 * (1.0 - t) * t * c2 + t * t * y2;
+ } else {
+ // scale [x2, x3] to [y2, y3] using a curve
+ float t = (nits - x2) / (x3 - x2);
+ nits = (1.0 - t) * (1.0 - t) * y2 +
+ 2.0 * (1.0 - t) * t * c3 + t * t * y3;
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ default:
+ // For completeness, this is tone-mapping from SDR to SDR, where this is
+ // just a no-op.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return xyz.y;
+ }
+ )");
+ break;
+ }
+ break;
+ }
+
+ program.append(R"(
+ float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
+ if (xyz.y <= 0.0) {
+ return 1.0;
+ }
+ return libtonemap_ToneMapTargetNits(xyz) / xyz.y;
+ }
+ )");
+ return program;
+ }
+
+ std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
+ std::vector<ShaderUniform> uniforms;
+
+ uniforms.reserve(2);
+
+ uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
+ .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
+ uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
+ .value = buildUniformValue<float>(metadata.contentMaxLuminance)});
+ return uniforms;
+ }
+
+ double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 /* linearRGB */, vec3 xyz, const Metadata& metadata) override {
+ if (xyz.y <= 0.0) {
+ return 1.0;
+ }
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ double targetNits = 0.0;
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ targetNits = xyz.y;
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ targetNits = std::clamp(xyz.y, 0.0f, 1000.0f);
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+
+ targetNits = xyz.y;
+ // if the max input luminance is less than what we can output then
+ // no tone mapping is needed as all color values will be in range.
+ if (metadata.contentMaxLuminance > metadata.displayMaxLuminance) {
+ // three control points
+ const double x0 = 10.0;
+ const double y0 = 17.0;
+ double x1 = metadata.displayMaxLuminance * 0.75;
+ double y1 = x1;
+ double x2 = x1 + (metadata.contentMaxLuminance - x1) / 2.0;
+ double y2 = y1 + (metadata.displayMaxLuminance - y1) * 0.75;
+
+ // horizontal distances between the last three control points
+ double h12 = x2 - x1;
+ double h23 = metadata.contentMaxLuminance - x2;
+ // tangents at the last three control points
+ double m1 = (y2 - y1) / h12;
+ double m3 = (metadata.displayMaxLuminance - y2) / h23;
+ double m2 = (m1 + m3) / 2.0;
+
+ if (targetNits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ double slope = y0 / x0;
+ targetNits *= slope;
+ } else if (targetNits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ double slope = (y1 - y0) / (x1 - x0);
+ targetNits = y0 + (targetNits - x0) * slope;
+ } else if (targetNits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ double t = (targetNits - x1) / h12;
+ targetNits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) *
+ (1.0 - t) +
+ (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+ } else {
+ // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+ double t = (targetNits - x2) / h23;
+ targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) *
+ (1.0 - t) +
+ (metadata.displayMaxLuminance * (3.0 - 2.0 * t) +
+ h23 * m3 * (t - 1.0)) *
+ t * t;
+ }
+ }
+ break;
+ }
+ break;
+ default:
+ // source is SDR
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG: {
+ // Map from SDR onto an HDR output buffer
+ // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
+ // [0, maxOutLumi] which is hard-coded to be 3000 nits.
+ const double maxOutLumi = 3000.0;
+
+ double x0 = 5.0;
+ double y0 = 2.5;
+ double x1 = metadata.displayMaxLuminance * 0.7;
+ double y1 = maxOutLumi * 0.15;
+ double x2 = metadata.displayMaxLuminance * 0.9;
+ double y2 = maxOutLumi * 0.45;
+ double x3 = metadata.displayMaxLuminance;
+ double y3 = maxOutLumi;
+
+ double c1 = y1 / 3.0;
+ double c2 = y2 / 2.0;
+ double c3 = y3 / 1.5;
+
+ targetNits = xyz.y;
+
+ if (targetNits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ double slope = y0 / x0;
+ targetNits *= slope;
+ } else if (targetNits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ double t = (targetNits - x0) / (x1 - x0);
+ targetNits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 +
+ t * t * y1;
+ } else if (targetNits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ double t = (targetNits - x1) / (x2 - x1);
+ targetNits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 +
+ t * t * y2;
+ } else {
+ // scale [x2, x3] to [y2, y3] using a curve
+ double t = (targetNits - x2) / (x3 - x2);
+ targetNits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 +
+ t * t * y3;
+ }
+ } break;
+ default:
+ // For completeness, this is tone-mapping from SDR to SDR, where this is
+ // just a no-op.
+ targetNits = xyz.y;
+ break;
+ }
+ }
+
+ return targetNits / xyz.y;
+ }
+};
+
+class ToneMapper13 : public ToneMapper {
+private:
+ double OETF_ST2084(double nits) {
+ nits = nits / 10000.0;
+ double m1 = (2610.0 / 4096.0) / 4.0;
+ double m2 = (2523.0 / 4096.0) * 128.0;
+ double c1 = (3424.0 / 4096.0);
+ double c2 = (2413.0 / 4096.0) * 32.0;
+ double c3 = (2392.0 / 4096.0) * 32.0;
+
+ double tmp = std::pow(nits, m1);
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return std::pow(tmp, m2);
+ }
+
+ double OETF_HLG(double nits) {
+ nits = nits / 1000.0;
+ const double a = 0.17883277;
+ const double b = 0.28466892;
+ const double c = 0.55991073;
+ return nits <= 1.0 / 12.0 ? std::sqrt(3.0 * nits) : a * std::log(12.0 * nits - b) + c;
+ }
+
+public:
+ std::string generateTonemapGainShaderSkSL(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ std::string program;
+ // Input uniforms
+ program.append(R"(
+ uniform float in_libtonemap_displayMaxLuminance;
+ uniform float in_libtonemap_inputMaxLuminance;
+ )");
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return maxRGB;
+ }
+ )");
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return clamp(maxRGB, 0.0, 1000.0);
+ }
+ )");
+ break;
+
+ default:
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_OETFTone(float channel) {
+ channel = channel / 10000.0;
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float tmp = pow(channel, float(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, float(m2));
+ }
+ )");
+ break;
+ case kTransferHLG:
+ program.append(R"(
+ float libtonemap_OETFTone(float channel) {
+ channel = channel / 1000.0;
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+ a * log(12.0 * channel - b) + c;
+ }
+ )");
+ break;
+ }
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ float maxInLumi = in_libtonemap_inputMaxLuminance;
+ float maxOutLumi = in_libtonemap_displayMaxLuminance;
+
+ float nits = maxRGB;
+
+ float x1 = maxOutLumi * 0.65;
+ float y1 = x1;
+
+ float x3 = maxInLumi;
+ float y3 = maxOutLumi;
+
+ float x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+ float y2 = maxOutLumi * 0.9;
+
+ float greyNorm1 = libtonemap_OETFTone(x1);
+ float greyNorm2 = libtonemap_OETFTone(x2);
+ float greyNorm3 = libtonemap_OETFTone(x3);
+
+ float slope1 = 0;
+ float slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+ float slope3 = (y3 - y2 ) / (greyNorm3 - greyNorm2);
+
+ if (nits < x1) {
+ return nits;
+ }
+
+ if (nits > maxInLumi) {
+ return maxOutLumi;
+ }
+
+ float greyNits = libtonemap_OETFTone(nits);
+
+ if (greyNits <= greyNorm2) {
+ nits = (greyNits - greyNorm2) * slope2 + y2;
+ } else if (greyNits <= greyNorm3) {
+ nits = (greyNits - greyNorm3) * slope3 + y3;
+ } else {
+ nits = maxOutLumi;
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ }
+ break;
+ default:
+ // Inverse tone-mapping and SDR-SDR mapping is not supported.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return maxRGB;
+ }
+ )");
+ break;
+ }
+
+ program.append(R"(
+ float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
+ float maxRGB = max(linearRGB.r, max(linearRGB.g, linearRGB.b));
+ if (maxRGB <= 0.0) {
+ return 1.0;
+ }
+ return libtonemap_ToneMapTargetNits(maxRGB) / maxRGB;
+ }
+ )");
+ return program;
+ }
+
+ std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
+ // Hardcode the max content luminance to a "reasonable" level
+ static const constexpr float kContentMaxLuminance = 4000.f;
+ std::vector<ShaderUniform> uniforms;
+ uniforms.reserve(2);
+ uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
+ .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
+ uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
+ .value = buildUniformValue<float>(kContentMaxLuminance)});
+ return uniforms;
+ }
+
+ double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 linearRGB, vec3 /* xyz */, const Metadata& metadata) override {
+ double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b});
+
+ if (maxRGB <= 0.0) {
+ return 1.0;
+ }
+
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ double targetNits = 0.0;
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ targetNits = maxRGB;
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ targetNits = std::clamp(maxRGB, 0.0, 1000.0);
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+
+ double maxInLumi = 4000;
+ double maxOutLumi = metadata.displayMaxLuminance;
+
+ targetNits = maxRGB;
+
+ double x1 = maxOutLumi * 0.65;
+ double y1 = x1;
+
+ double x3 = maxInLumi;
+ double y3 = maxOutLumi;
+
+ double x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+ double y2 = maxOutLumi * 0.9;
+
+ double greyNorm1 = 0.0;
+ double greyNorm2 = 0.0;
+ double greyNorm3 = 0.0;
+
+ if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
+ greyNorm1 = OETF_ST2084(x1);
+ greyNorm2 = OETF_ST2084(x2);
+ greyNorm3 = OETF_ST2084(x3);
+ } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+ greyNorm1 = OETF_HLG(x1);
+ greyNorm2 = OETF_HLG(x2);
+ greyNorm3 = OETF_HLG(x3);
+ }
+
+ double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+ double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2);
+
+ if (targetNits < x1) {
+ break;
+ }
+
+ if (targetNits > maxInLumi) {
+ targetNits = maxOutLumi;
+ break;
+ }
+
+ double greyNits = 0.0;
+ if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
+ greyNits = OETF_ST2084(targetNits);
+ } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+ greyNits = OETF_HLG(targetNits);
+ }
+
+ if (greyNits <= greyNorm2) {
+ targetNits = (greyNits - greyNorm2) * slope2 + y2;
+ } else if (greyNits <= greyNorm3) {
+ targetNits = (greyNits - greyNorm3) * slope3 + y3;
+ } else {
+ targetNits = maxOutLumi;
+ }
+ break;
+ }
+ break;
+ default:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ default:
+ targetNits = maxRGB;
+ break;
+ }
+ break;
+ }
+
+ return targetNits / maxRGB;
+ }
+};
+
+} // namespace
+
+ToneMapper* getToneMapper() {
+ static std::once_flag sOnce;
+ static std::unique_ptr<ToneMapper> sToneMapper;
+
+ std::call_once(sOnce, [&] {
+ switch (kToneMapAlgorithm) {
+ case ToneMapAlgorithm::AndroidO:
+ sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapperO());
+ break;
+ case ToneMapAlgorithm::Android13:
+ sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapper13());
+ }
+ });
+
+ return sToneMapper.get();
+}
+} // namespace android::tonemap \ No newline at end of file
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 506e308370..f5a22ec272 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -29,6 +29,11 @@ license {
],
}
+cc_library_headers {
+ name: "libui_fuzzableDataspaces_headers",
+ export_include_dirs: ["include/ui/fuzzer/"],
+}
+
cc_defaults {
name: "libui-defaults",
clang: true,
@@ -86,6 +91,7 @@ cc_library_static {
export_include_dirs: [
"include",
+ "include_mock",
"include_private",
"include_types",
],
@@ -122,6 +128,7 @@ cc_library_shared {
srcs: [
"DebugUtils.cpp",
"DeviceProductInfo.cpp",
+ "DisplayIdentification.cpp",
"DisplayMode.cpp",
"DynamicDisplayInfo.cpp",
"Fence.cpp",
@@ -137,7 +144,7 @@ cc_library_shared {
"HdrCapabilities.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
- "Size.cpp",
+ "StaticAsserts.cpp",
"StaticDisplayInfo.cpp",
],
@@ -159,13 +166,15 @@ cc_library_shared {
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.allocator@4.0",
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.allocator-V1-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
"libbase",
+ "libbinder_ndk",
"libcutils",
"libgralloctypes",
"libhidlbase",
@@ -176,12 +185,13 @@ cc_library_shared {
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libgralloctypes",
],
static_libs: [
+ "libaidlcommonsupport",
"libarect",
"libgrallocusage",
"libmath",
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 1f006ceb69..073da89758 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -302,6 +302,8 @@ std::string decodePixelFormat(android::PixelFormat format) {
return std::string("RGB_565");
case android::PIXEL_FORMAT_BGRA_8888:
return std::string("BGRA_8888");
+ case android::PIXEL_FORMAT_R_8:
+ return std::string("R_8");
default:
return StringPrintf("Unknown %#08x", format);
}
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
new file mode 100644
index 0000000000..16ed82af7c
--- /dev/null
+++ b/libs/ui/DisplayIdentification.cpp
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "DisplayIdentification"
+
+#include <algorithm>
+#include <cctype>
+#include <numeric>
+#include <optional>
+
+#include <log/log.h>
+
+#include <ui/DisplayIdentification.h>
+
+namespace android {
+namespace {
+
+template <class T>
+inline T load(const void* p) {
+ static_assert(std::is_integral<T>::value, "T must be integral");
+
+ T r;
+ std::memcpy(&r, p, sizeof(r));
+ return r;
+}
+
+uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
+ return (val >> shift) | (val << (64 - shift));
+}
+
+uint64_t shiftMix(uint64_t val) {
+ return val ^ (val >> 47);
+}
+
+uint64_t hash64Len16(uint64_t u, uint64_t v) {
+ constexpr uint64_t kMul = 0x9ddfea08eb382d69;
+ uint64_t a = (u ^ v) * kMul;
+ a ^= (a >> 47);
+ uint64_t b = (v ^ a) * kMul;
+ b ^= (b >> 47);
+ b *= kMul;
+ return b;
+}
+
+uint64_t hash64Len0To16(const char* s, uint64_t len) {
+ constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
+ constexpr uint64_t k3 = 0xc949d7c7509e6557;
+
+ if (len > 8) {
+ const uint64_t a = load<uint64_t>(s);
+ const uint64_t b = load<uint64_t>(s + len - 8);
+ return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
+ }
+ if (len >= 4) {
+ const uint32_t a = load<uint32_t>(s);
+ const uint32_t b = load<uint32_t>(s + len - 4);
+ return hash64Len16(len + (a << 3), b);
+ }
+ if (len > 0) {
+ const unsigned char a = static_cast<unsigned char>(s[0]);
+ const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
+ const unsigned char c = static_cast<unsigned char>(s[len - 1]);
+ const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
+ const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
+ return shiftMix(y * k2 ^ z * k3) * k2;
+ }
+ return k2;
+}
+
+using byte_view = std::basic_string_view<uint8_t>;
+
+constexpr size_t kEdidBlockSize = 128;
+constexpr size_t kEdidHeaderLength = 5;
+
+constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu;
+
+std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
+ if (view.size() < kEdidHeaderLength || view[0] || view[1] || view[2] || view[4]) {
+ return {};
+ }
+
+ return view[3];
+}
+
+std::string_view parseEdidText(const byte_view& view) {
+ std::string_view text(reinterpret_cast<const char*>(view.data()), view.size());
+ text = text.substr(0, text.find('\n'));
+
+ if (!std::all_of(text.begin(), text.end(), ::isprint)) {
+ ALOGW("Invalid EDID: ASCII text is not printable.");
+ return {};
+ }
+
+ return text;
+}
+
+// Big-endian 16-bit value encodes three 5-bit letters where A is 0b00001.
+template <size_t I>
+char getPnpLetter(uint16_t id) {
+ static_assert(I < 3);
+ const char letter = 'A' + (static_cast<uint8_t>(id >> ((2 - I) * 5)) & 0b00011111) - 1;
+ return letter < 'A' || letter > 'Z' ? '\0' : letter;
+}
+
+DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
+ DeviceProductInfo info;
+ info.name.assign(edid.displayName);
+ info.productId = std::to_string(edid.productId);
+ info.manufacturerPnpId = edid.pnpId;
+
+ constexpr uint8_t kModelYearFlag = 0xff;
+ constexpr uint32_t kYearOffset = 1990;
+
+ const auto year = edid.manufactureOrModelYear + kYearOffset;
+ if (edid.manufactureWeek == kModelYearFlag) {
+ info.manufactureOrModelDate = DeviceProductInfo::ModelYear{.year = year};
+ } else if (edid.manufactureWeek == 0) {
+ DeviceProductInfo::ManufactureYear date;
+ date.year = year;
+ info.manufactureOrModelDate = date;
+ } else {
+ DeviceProductInfo::ManufactureWeekAndYear date;
+ date.year = year;
+ date.week = edid.manufactureWeek;
+ info.manufactureOrModelDate = date;
+ }
+
+ if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
+ const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
+ info.relativeAddress = {address.a, address.b, address.c, address.d};
+ }
+ return info;
+}
+
+Cea861ExtensionBlock parseCea861Block(const byte_view& block) {
+ Cea861ExtensionBlock cea861Block;
+
+ constexpr size_t kRevisionNumberOffset = 1;
+ cea861Block.revisionNumber = block[kRevisionNumberOffset];
+
+ constexpr size_t kDetailedTimingDescriptorsOffset = 2;
+ const size_t dtdStart =
+ std::min(kEdidBlockSize, static_cast<size_t>(block[kDetailedTimingDescriptorsOffset]));
+
+ // Parse data blocks.
+ for (size_t dataBlockOffset = 4; dataBlockOffset < dtdStart;) {
+ const uint8_t header = block[dataBlockOffset];
+ const uint8_t tag = header >> 5;
+ const size_t bodyLength = header & 0b11111;
+ constexpr size_t kDataBlockHeaderSize = 1;
+ const size_t dataBlockSize = bodyLength + kDataBlockHeaderSize;
+
+ if (block.size() < dataBlockOffset + dataBlockSize) {
+ ALOGW("Invalid EDID: CEA 861 data block is truncated.");
+ break;
+ }
+
+ const byte_view dataBlock(block.data() + dataBlockOffset, dataBlockSize);
+ constexpr uint8_t kVendorSpecificDataBlockTag = 0x3;
+
+ if (tag == kVendorSpecificDataBlockTag) {
+ const uint32_t ieeeRegistrationId = static_cast<uint32_t>(
+ dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16));
+ constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03;
+
+ if (ieeeRegistrationId == kHdmiIeeeRegistrationId) {
+ const uint8_t a = dataBlock[4] >> 4;
+ const uint8_t b = dataBlock[4] & 0b1111;
+ const uint8_t c = dataBlock[5] >> 4;
+ const uint8_t d = dataBlock[5] & 0b1111;
+ cea861Block.hdmiVendorDataBlock =
+ HdmiVendorDataBlock{.physicalAddress = HdmiPhysicalAddress{a, b, c, d}};
+ } else {
+ ALOGV("Ignoring vendor specific data block for vendor with IEEE OUI %x",
+ ieeeRegistrationId);
+ }
+ } else {
+ ALOGV("Ignoring CEA-861 data block with tag %x", tag);
+ }
+ dataBlockOffset += bodyLength + kDataBlockHeaderSize;
+ }
+
+ return cea861Block;
+}
+
+} // namespace
+
+bool isEdid(const DisplayIdentificationData& data) {
+ const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
+ return data.size() >= sizeof(kMagic) &&
+ std::equal(std::begin(kMagic), std::end(kMagic), data.begin());
+}
+
+std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
+ if (edid.size() < kEdidBlockSize) {
+ ALOGW("Invalid EDID: structure is truncated.");
+ // Attempt parsing even if EDID is malformed.
+ } else {
+ ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kEdidBlockSize,
+ static_cast<uint8_t>(0)),
+ "Invalid EDID: structure does not checksum.");
+ }
+
+ constexpr size_t kManufacturerOffset = 8;
+ if (edid.size() < kManufacturerOffset + sizeof(uint16_t)) {
+ ALOGE("Invalid EDID: manufacturer ID is truncated.");
+ return {};
+ }
+
+ // Plug and play ID encoded as big-endian 16-bit value.
+ const uint16_t manufacturerId =
+ static_cast<uint16_t>((edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]);
+
+ const auto pnpId = getPnpId(manufacturerId);
+ if (!pnpId) {
+ ALOGE("Invalid EDID: manufacturer ID is not a valid PnP ID.");
+ return {};
+ }
+
+ constexpr size_t kProductIdOffset = 10;
+ if (edid.size() < kProductIdOffset + sizeof(uint16_t)) {
+ ALOGE("Invalid EDID: product ID is truncated.");
+ return {};
+ }
+ const uint16_t productId =
+ static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8));
+
+ constexpr size_t kManufactureWeekOffset = 16;
+ if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
+ ALOGE("Invalid EDID: manufacture week is truncated.");
+ return {};
+ }
+ const uint8_t manufactureWeek = edid[kManufactureWeekOffset];
+ ALOGW_IF(0x37 <= manufactureWeek && manufactureWeek <= 0xfe,
+ "Invalid EDID: week of manufacture cannot be in the range [0x37, 0xfe].");
+
+ constexpr size_t kManufactureYearOffset = 17;
+ if (edid.size() < kManufactureYearOffset + sizeof(uint8_t)) {
+ ALOGE("Invalid EDID: manufacture year is truncated.");
+ return {};
+ }
+ const uint8_t manufactureOrModelYear = edid[kManufactureYearOffset];
+ ALOGW_IF(manufactureOrModelYear <= 0xf,
+ "Invalid EDID: model year or manufacture year cannot be in the range [0x0, 0xf].");
+
+ constexpr size_t kDescriptorOffset = 54;
+ if (edid.size() < kDescriptorOffset) {
+ ALOGE("Invalid EDID: descriptors are missing.");
+ return {};
+ }
+
+ byte_view view(edid.data(), edid.size());
+ view.remove_prefix(kDescriptorOffset);
+
+ std::string_view displayName;
+ std::string_view serialNumber;
+ std::string_view asciiText;
+
+ constexpr size_t kDescriptorCount = 4;
+ constexpr size_t kDescriptorLength = 18;
+
+ for (size_t i = 0; i < kDescriptorCount; i++) {
+ if (view.size() < kDescriptorLength) {
+ break;
+ }
+
+ if (const auto type = getEdidDescriptorType(view)) {
+ byte_view descriptor(view.data(), kDescriptorLength);
+ descriptor.remove_prefix(kEdidHeaderLength);
+
+ switch (*type) {
+ case 0xfc:
+ displayName = parseEdidText(descriptor);
+ break;
+ case 0xfe:
+ asciiText = parseEdidText(descriptor);
+ break;
+ case 0xff:
+ serialNumber = parseEdidText(descriptor);
+ break;
+ }
+ }
+
+ view.remove_prefix(kDescriptorLength);
+ }
+
+ std::string_view modelString = displayName;
+
+ if (modelString.empty()) {
+ ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
+ modelString = serialNumber;
+ }
+ if (modelString.empty()) {
+ ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
+ modelString = asciiText;
+ }
+ if (modelString.empty()) {
+ ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
+ return {};
+ }
+
+ // Hash model string instead of using product code or (integer) serial number, since the latter
+ // have been observed to change on some displays with multiple inputs. Use a stable hash instead
+ // of std::hash which is only required to be same within a single execution of a program.
+ const uint32_t modelHash = static_cast<uint32_t>(cityHash64Len0To16(modelString));
+
+ // Parse extension blocks.
+ std::optional<Cea861ExtensionBlock> cea861Block;
+ if (edid.size() < kEdidBlockSize) {
+ ALOGW("Invalid EDID: block 0 is truncated.");
+ } else {
+ constexpr size_t kNumExtensionsOffset = 126;
+ const size_t numExtensions = edid[kNumExtensionsOffset];
+ view = byte_view(edid.data(), edid.size());
+ for (size_t blockNumber = 1; blockNumber <= numExtensions; blockNumber++) {
+ view.remove_prefix(kEdidBlockSize);
+ if (view.size() < kEdidBlockSize) {
+ ALOGW("Invalid EDID: block %zu is truncated.", blockNumber);
+ break;
+ }
+
+ const byte_view block(view.data(), kEdidBlockSize);
+ ALOGW_IF(std::accumulate(block.begin(), block.end(), static_cast<uint8_t>(0)),
+ "Invalid EDID: block %zu does not checksum.", blockNumber);
+ const uint8_t tag = block[0];
+
+ constexpr uint8_t kCea861BlockTag = 0x2;
+ if (tag == kCea861BlockTag) {
+ cea861Block = parseCea861Block(block);
+ } else {
+ ALOGV("Ignoring block number %zu with tag %x.", blockNumber, tag);
+ }
+ }
+ }
+
+ return Edid{.manufacturerId = manufacturerId,
+ .productId = productId,
+ .pnpId = *pnpId,
+ .modelHash = modelHash,
+ .displayName = displayName,
+ .manufactureOrModelYear = manufactureOrModelYear,
+ .manufactureWeek = manufactureWeek,
+ .cea861Block = cea861Block};
+}
+
+std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
+ const char a = getPnpLetter<0>(manufacturerId);
+ const char b = getPnpLetter<1>(manufacturerId);
+ const char c = getPnpLetter<2>(manufacturerId);
+ return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
+}
+
+std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) {
+ return getPnpId(displayId.getManufacturerId());
+}
+
+std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
+ uint8_t port, const DisplayIdentificationData& data) {
+ if (!isEdid(data)) {
+ ALOGE("Display identification data has unknown format.");
+ return {};
+ }
+
+ const auto edid = parseEdid(data);
+ if (!edid) {
+ return {};
+ }
+
+ const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
+ return DisplayIdentificationInfo{.id = displayId,
+ .name = std::string(edid->displayName),
+ .deviceProductInfo = buildDeviceProductInfo(*edid)};
+}
+
+PhysicalDisplayId getVirtualDisplayId(uint32_t id) {
+ return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
+}
+
+uint64_t cityHash64Len0To16(std::string_view sv) {
+ auto len = sv.length();
+ if (len > 16) {
+ ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
+ len = 16;
+ }
+ return hash64Len0To16(sv.data(), len);
+}
+
+} // namespace android \ No newline at end of file
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 80f6c82bb0..1f8a2f058f 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -16,6 +16,12 @@
#define LOG_TAG "Gralloc4"
+#include <aidl/android/hardware/graphics/allocator/AllocationError.h>
+#include <aidl/android/hardware/graphics/allocator/AllocationResult.h>
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android/binder_enums.h>
+#include <android/binder_manager.h>
#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
#include <ui/Gralloc4.h>
@@ -27,6 +33,8 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
+using aidl::android::hardware::graphics::allocator::AllocationError;
+using aidl::android::hardware::graphics::allocator::AllocationResult;
using aidl::android::hardware::graphics::common::ExtendableType;
using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
using aidl::android::hardware::graphics::common::StandardMetadataType;
@@ -36,7 +44,10 @@ using android::hardware::graphics::common::V1_2::BufferUsage;
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::IMapper;
+using AidlIAllocator = ::aidl::android::hardware::graphics::allocator::IAllocator;
+using AidlBufferUsage = ::aidl::android::hardware::graphics::common::BufferUsage;
using AidlDataspace = ::aidl::android::hardware::graphics::common::Dataspace;
+using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle;
using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
@@ -48,6 +59,7 @@ namespace android {
namespace {
static constexpr Error kTransactionError = Error::NO_RESOURCES;
+static const auto kAidlAllocatorServiceName = AidlIAllocator::descriptor + std::string("/default");
uint64_t getValidUsageBits() {
static const uint64_t validUsageBits = []() -> uint64_t {
@@ -61,6 +73,17 @@ uint64_t getValidUsageBits() {
return validUsageBits;
}
+uint64_t getValidUsageBits41() {
+ static const uint64_t validUsageBits = []() -> uint64_t {
+ uint64_t bits = 0;
+ for (const auto bit : ndk::enum_range<AidlBufferUsage>{}) {
+ bits |= static_cast<int64_t>(bit);
+ }
+ return bits;
+ }();
+ return validUsageBits;
+}
+
static inline IMapper::Rect sGralloc4Rect(const Rect& rect) {
IMapper::Rect outRect{};
outRect.left = rect.left;
@@ -81,6 +104,21 @@ static inline void sBufferDescriptorInfo(std::string name, uint32_t width, uint3
outDescriptorInfo->reservedSize = 0;
}
+// See if gralloc "4.1" is available.
+static bool hasIAllocatorAidl() {
+ // Avoid re-querying repeatedly for this information;
+ static bool sHasIAllocatorAidl = []() -> bool {
+ // TODO: Enable after landing sepolicy changes
+ if constexpr ((true)) return false;
+
+ if (__builtin_available(android 31, *)) {
+ return AServiceManager_isDeclared(kAidlAllocatorServiceName.c_str());
+ }
+ return false;
+ }();
+ return sHasIAllocatorAidl;
+}
+
} // anonymous namespace
void Gralloc4Mapper::preload() {
@@ -105,6 +143,9 @@ bool Gralloc4Mapper::isLoaded() const {
status_t Gralloc4Mapper::validateBufferDescriptorInfo(
IMapper::BufferDescriptorInfo* descriptorInfo) const {
uint64_t validUsageBits = getValidUsageBits();
+ if (hasIAllocatorAidl()) {
+ validUsageBits |= getValidUsageBits41();
+ }
if (descriptorInfo->usage & ~validUsageBits) {
ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
@@ -634,6 +675,12 @@ status_t Gralloc4Mapper::getSmpte2094_40(
outSmpte2094_40);
}
+status_t Gralloc4Mapper::getSmpte2094_10(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) const {
+ return get(bufferHandle, gralloc4::MetadataType_Smpte2094_10, gralloc4::decodeSmpte2094_10,
+ outSmpte2094_10);
+}
+
template <class T>
status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
@@ -935,9 +982,10 @@ status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ost
}
double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;
- *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << allocationSizeKiB
- << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
- << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
+ *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << std::fixed
+ << allocationSizeKiB << "KiB, w/h:" << width << "x" << height << ", usage: 0x"
+ << std::hex << usage << std::dec
+ << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
<< ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
<< ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace) << std::dec
<< ", compressed: ";
@@ -1063,6 +1111,13 @@ Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(map
ALOGW("allocator 4.x is not supported");
return;
}
+ if (__builtin_available(android 31, *)) {
+ if (hasIAllocatorAidl()) {
+ mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
+ AServiceManager_waitForService(kAidlAllocatorServiceName.c_str())));
+ ALOGE_IF(!mAidlAllocator, "AIDL IAllocator declared but failed to get service");
+ }
+ }
}
bool Gralloc4Allocator::isLoaded() const {
@@ -1087,6 +1142,52 @@ status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width,
return error;
}
+ if (mAidlAllocator) {
+ AllocationResult result;
+ auto status = mAidlAllocator->allocate(descriptor, bufferCount, &result);
+ if (!status.isOk()) {
+ error = status.getExceptionCode();
+ if (error == EX_SERVICE_SPECIFIC) {
+ error = status.getServiceSpecificError();
+ }
+ if (error == OK) {
+ error = UNKNOWN_ERROR;
+ }
+ } else {
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(makeFromAidl(result.buffers[i]),
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ break;
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = dupFromAidl(result.buffers[i]);
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
+ }
+ }
+ }
+ *outStride = result.stride;
+ // Release all the resources held by AllocationResult (specifically any remaining FDs)
+ result = {};
+ // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+ hardware::IPCThreadState::self()->flushCommands();
+ return error;
+ }
+
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index d20bd7a899..82d6cd5ba3 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -301,6 +301,11 @@ status_t GraphicBufferMapper::getSmpte2094_40(
return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
}
+status_t GraphicBufferMapper::getSmpte2094_10(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) {
+ return mMapper->getSmpte2094_10(bufferHandle, outSmpte2094_10);
+}
+
status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount,
uint64_t usage,
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index e88fdd5e84..799fbc9d54 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -35,25 +35,8 @@ uint32_t bytesPerPixel(PixelFormat format) {
case PIXEL_FORMAT_RGBA_5551:
case PIXEL_FORMAT_RGBA_4444:
return 2;
- }
- return 0;
-}
-
-uint32_t bitsPerPixel(PixelFormat format) {
- switch (format) {
- case PIXEL_FORMAT_RGBA_FP16:
- return 64;
- case PIXEL_FORMAT_RGBA_8888:
- case PIXEL_FORMAT_RGBX_8888:
- case PIXEL_FORMAT_BGRA_8888:
- case PIXEL_FORMAT_RGBA_1010102:
- return 32;
- case PIXEL_FORMAT_RGB_888:
- return 24;
- case PIXEL_FORMAT_RGB_565:
- case PIXEL_FORMAT_RGBA_5551:
- case PIXEL_FORMAT_RGBA_4444:
- return 16;
+ case PIXEL_FORMAT_R_8:
+ return 1;
}
return 0;
}
diff --git a/libs/ui/StaticAsserts.cpp b/libs/ui/StaticAsserts.cpp
new file mode 100644
index 0000000000..85da64f10e
--- /dev/null
+++ b/libs/ui/StaticAsserts.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/PixelFormat.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+
+// Ideally, PIXEL_FORMAT_R_8 would simply be defined to match the aidl PixelFormat, but
+// PixelFormat.h (where PIXEL_FORMAT_R_8 is defined) is pulled in by builds for
+// which there is no aidl build (e.g. Windows).
+static_assert(android::PIXEL_FORMAT_R_8 ==static_cast<int32_t>(
+ aidl::android::hardware::graphics::common::PixelFormat::R_8));
diff --git a/libs/ui/StaticDisplayInfo.cpp b/libs/ui/StaticDisplayInfo.cpp
index b66b281394..03d15e4694 100644
--- a/libs/ui/StaticDisplayInfo.cpp
+++ b/libs/ui/StaticDisplayInfo.cpp
@@ -29,7 +29,8 @@ size_t StaticDisplayInfo::getFlattenedSize() const {
return FlattenableHelpers::getFlattenedSize(connectionType) +
FlattenableHelpers::getFlattenedSize(density) +
FlattenableHelpers::getFlattenedSize(secure) +
- FlattenableHelpers::getFlattenedSize(deviceProductInfo);
+ FlattenableHelpers::getFlattenedSize(deviceProductInfo) +
+ FlattenableHelpers::getFlattenedSize(installOrientation);
}
status_t StaticDisplayInfo::flatten(void* buffer, size_t size) const {
@@ -40,6 +41,7 @@ status_t StaticDisplayInfo::flatten(void* buffer, size_t size) const {
RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density));
RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure));
RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, installOrientation));
return OK;
}
@@ -48,6 +50,7 @@ status_t StaticDisplayInfo::unflatten(void const* buffer, size_t size) {
RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density));
RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure));
RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &installOrientation));
return OK;
}
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index cd68c1c0ec..b34d90699f 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -396,6 +396,11 @@ Transform Transform::inverse() const {
result.mMatrix[1][0] = -b*idet;
result.mMatrix[1][1] = a*idet;
result.mType = mType;
+ if (getOrientation() & ROT_90) {
+ // Recalculate the type if there is a 90-degree rotation component, since the inverse
+ // of ROT_90 is ROT_270 and vice versa.
+ result.mType |= UNKNOWN_TYPE;
+ }
vec2 T(-x, -y);
T = result.transform(T);
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index f196ab901a..9120972a42 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -38,12 +38,22 @@ struct DisplayId {
uint64_t value;
+ // For deserialization.
+ static constexpr std::optional<DisplayId> fromValue(uint64_t);
+
+ // As above, but also upcast to Id.
+ template <typename Id>
+ static constexpr std::optional<Id> fromValue(uint64_t value) {
+ if (const auto id = Id::tryCast(DisplayId(value))) {
+ return id;
+ }
+ return {};
+ }
+
protected:
explicit constexpr DisplayId(uint64_t id) : value(id) {}
};
-static_assert(sizeof(DisplayId) == sizeof(uint64_t));
-
inline bool operator==(DisplayId lhs, DisplayId rhs) {
return lhs.value == rhs.value;
}
@@ -80,11 +90,8 @@ struct PhysicalDisplayId : DisplayId {
// TODO(b/162612135) Remove default constructor
PhysicalDisplayId() = default;
- // TODO(b/162612135) Remove constructor
- explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
-
constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
private:
@@ -96,10 +103,9 @@ private:
explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
};
-static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
-
struct VirtualDisplayId : DisplayId {
using BaseId = uint32_t;
+
// Flag indicating that this virtual display is backed by the GPU.
static constexpr uint64_t FLAG_GPU = 1ULL << 61;
@@ -163,10 +169,23 @@ private:
explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
};
+constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) {
+ if (const auto id = fromValue<PhysicalDisplayId>(value)) {
+ return id;
+ }
+ if (const auto id = fromValue<VirtualDisplayId>(value)) {
+ return id;
+ }
+ return {};
+}
+
+static_assert(sizeof(DisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+
+static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
-static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
} // namespace android
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
new file mode 100644
index 0000000000..fc9c0f491b
--- /dev/null
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <ui/DeviceProductInfo.h>
+#include <ui/DisplayId.h>
+
+#define LEGACY_DISPLAY_TYPE_PRIMARY 0
+#define LEGACY_DISPLAY_TYPE_EXTERNAL 1
+
+namespace android {
+
+using DisplayIdentificationData = std::vector<uint8_t>;
+
+struct DisplayIdentificationInfo {
+ PhysicalDisplayId id;
+ std::string name;
+ std::optional<DeviceProductInfo> deviceProductInfo;
+};
+
+struct ExtensionBlock {
+ uint8_t tag;
+ uint8_t revisionNumber;
+};
+
+struct HdmiPhysicalAddress {
+ // The address describes the path from the display sink in the network of connected HDMI
+ // devices. The format of the address is "a.b.c.d". For example, address 2.1.0.0 means we are
+ // connected to port 1 of a device which is connected to port 2 of the sink.
+ uint8_t a, b, c, d;
+};
+
+struct HdmiVendorDataBlock {
+ HdmiPhysicalAddress physicalAddress;
+};
+
+struct Cea861ExtensionBlock : ExtensionBlock {
+ std::optional<HdmiVendorDataBlock> hdmiVendorDataBlock;
+};
+
+struct Edid {
+ uint16_t manufacturerId;
+ uint16_t productId;
+ PnpId pnpId;
+ uint32_t modelHash;
+ std::string_view displayName;
+ uint8_t manufactureOrModelYear;
+ uint8_t manufactureWeek;
+ std::optional<Cea861ExtensionBlock> cea861Block;
+};
+
+bool isEdid(const DisplayIdentificationData&);
+std::optional<Edid> parseEdid(const DisplayIdentificationData&);
+std::optional<PnpId> getPnpId(uint16_t manufacturerId);
+std::optional<PnpId> getPnpId(PhysicalDisplayId);
+
+std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
+ uint8_t port, const DisplayIdentificationData&);
+
+PhysicalDisplayId getVirtualDisplayId(uint32_t id);
+
+// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
+uint64_t cityHash64Len0To16(std::string_view sv);
+
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
index 70a0d50611..98ee35652a 100644
--- a/libs/ui/include/ui/DisplayState.h
+++ b/libs/ui/include/ui/DisplayState.h
@@ -16,21 +16,18 @@
#pragma once
+#include <ui/LayerStack.h>
#include <ui/Rotation.h>
#include <ui/Size.h>
-#include <cstdint>
#include <type_traits>
namespace android::ui {
-using LayerStack = uint32_t;
-constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1);
-
// Transactional state of physical or virtual display. Note that libgui defines
// android::DisplayState as a superset of android::ui::DisplayState.
struct DisplayState {
- LayerStack layerStack = NO_LAYER_STACK;
+ LayerStack layerStack;
Rotation orientation = ROTATION_0;
Size layerStackSpaceRect;
};
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 6efecd3c0e..9aae145c04 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -26,6 +26,10 @@
namespace android {
+namespace mock {
+class MockFence;
+}
+
class String8;
// ===========================================================================
@@ -109,7 +113,7 @@ public:
// fence transitioned to the signaled state. If the fence is not signaled
// then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an
// error occurs then SIGNAL_TIME_INVALID is returned.
- nsecs_t getSignalTime() const;
+ virtual nsecs_t getSignalTime() const;
enum class Status {
Invalid, // Fence is invalid
@@ -120,7 +124,7 @@ public:
// getStatus() returns whether the fence has signaled yet. Prefer this to
// getSignalTime() or wait() if all you care about is whether the fence has
// signaled.
- inline Status getStatus() {
+ virtual inline Status getStatus() {
// The sync_wait call underlying wait() has been measured to be
// significantly faster than the sync_fence_info call underlying
// getSignalTime(), which might otherwise appear to be the more obvious
@@ -144,7 +148,10 @@ public:
private:
// Only allow instantiation using ref counting.
friend class LightRefBase<Fence>;
- ~Fence() = default;
+ virtual ~Fence() = default;
+
+ // Allow mocking for unit testing
+ friend class mock::MockFence;
base::unique_fd mFenceFd;
};
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index e199648a8b..753b0a6794 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -178,6 +178,11 @@ public:
std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
return INVALID_OPERATION;
}
+ virtual status_t getSmpte2094_10(
+ buffer_handle_t /*bufferHandle*/,
+ std::optional<std::vector<uint8_t>>* /*outSmpte2094_10*/) const {
+ return INVALID_OPERATION;
+ }
virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
PixelFormat /*format*/, uint32_t /*layerCount*/,
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 4729cbaf67..6bafcd6c81 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_UI_GRALLOC4_H
#define ANDROID_UI_GRALLOC4_H
+#include <aidl/android/hardware/graphics/allocator/IAllocator.h>
#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/mapper/4.0/IMapper.h>
@@ -108,6 +109,8 @@ public:
std::optional<ui::Cta861_3>* outCta861_3) const override;
status_t getSmpte2094_40(buffer_handle_t bufferHandle,
std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
+ status_t getSmpte2094_10(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_10) const override;
status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
@@ -202,6 +205,8 @@ public:
private:
const Gralloc4Mapper& mMapper;
sp<hardware::graphics::allocator::V4_0::IAllocator> mAllocator;
+ // Optional "4.1" allocator
+ std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAidlAllocator;
};
} // namespace android
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 837e3d826b..257c155efd 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -126,6 +126,8 @@ public:
status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
status_t getSmpte2094_40(buffer_handle_t bufferHandle,
std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+ status_t getSmpte2094_10(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_10);
/**
* Gets the default metadata for a gralloc buffer allocated with the given parameters.
diff --git a/libs/ui/include/ui/LayerStack.h b/libs/ui/include/ui/LayerStack.h
new file mode 100644
index 0000000000..d6ffeb7fad
--- /dev/null
+++ b/libs/ui/include/ui/LayerStack.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <ftl/cast.h>
+#include <ftl/string.h>
+#include <log/log.h>
+
+namespace android::ui {
+
+// A LayerStack identifies a Z-ordered group of layers. A layer can only be associated to a single
+// LayerStack, but a LayerStack can be associated to multiple displays, mirroring the same content.
+struct LayerStack {
+ uint32_t id = UINT32_MAX;
+
+ template <typename T>
+ static constexpr LayerStack fromValue(T v) {
+ if (ftl::cast_safety<uint32_t>(v) == ftl::CastSafety::kSafe) {
+ return {static_cast<uint32_t>(v)};
+ }
+
+ ALOGW("Invalid layer stack %s", ftl::to_string(v).c_str());
+ return {};
+ }
+};
+
+constexpr LayerStack INVALID_LAYER_STACK;
+constexpr LayerStack DEFAULT_LAYER_STACK{0u};
+
+inline bool operator==(LayerStack lhs, LayerStack rhs) {
+ return lhs.id == rhs.id;
+}
+
+inline bool operator!=(LayerStack lhs, LayerStack rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator>(LayerStack lhs, LayerStack rhs) {
+ return lhs.id > rhs.id;
+}
+
+// A LayerFilter determines if a layer is included for output to a display.
+struct LayerFilter {
+ LayerStack layerStack;
+
+ // True if the layer is only output to internal displays, i.e. excluded from screenshots, screen
+ // recordings, and mirroring to virtual or external displays. Used for display cutout overlays.
+ bool toInternalDisplay = false;
+
+ // Returns true if the input filter can be output to this filter.
+ bool includes(LayerFilter other) const {
+ // The layer stacks must match.
+ if (other.layerStack == INVALID_LAYER_STACK || other.layerStack != layerStack) {
+ return false;
+ }
+
+ // The output must be to an internal display if the input filter has that constraint.
+ return !other.toInternalDisplay || toInternalDisplay;
+ }
+};
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index 02773d92fc..f422ce439e 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -62,12 +62,12 @@ enum {
PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB
PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA
PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
+ PIXEL_FORMAT_R_8 = 0x38,
};
typedef int32_t PixelFormat;
uint32_t bytesPerPixel(PixelFormat format);
-uint32_t bitsPerPixel(PixelFormat format);
}; // namespace android
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index f1e825286e..ecc192dcae 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -23,103 +23,70 @@
#include <type_traits>
#include <utility>
-namespace android {
-namespace ui {
+namespace android::ui {
-// Forward declare a few things.
-struct Size;
-bool operator==(const Size& lhs, const Size& rhs);
-
-/**
- * A simple value type representing a two-dimensional size
- */
+// A simple value type representing a two-dimensional size.
struct Size {
- int32_t width;
- int32_t height;
+ int32_t width = -1;
+ int32_t height = -1;
- // Special values
- static const Size INVALID;
- static const Size EMPTY;
+ constexpr Size() = default;
- // ------------------------------------------------------------------------
- // Construction
- // ------------------------------------------------------------------------
-
- Size() : Size(INVALID) {}
template <typename T>
- Size(T&& w, T&& h)
- : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
- height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
-
- // ------------------------------------------------------------------------
- // Accessors
- // ------------------------------------------------------------------------
+ constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {}
int32_t getWidth() const { return width; }
int32_t getHeight() const { return height; }
+ // Valid means non-negative width and height
+ bool isValid() const { return width >= 0 && height >= 0; }
+
+ // Empty means zero width and height
+ bool isEmpty() const;
+
template <typename T>
- void setWidth(T&& v) {
- width = Size::clamp<int32_t, T>(std::forward<T>(v));
+ void setWidth(T v) {
+ width = clamp<int32_t>(v);
}
+
template <typename T>
- void setHeight(T&& v) {
- height = Size::clamp<int32_t, T>(std::forward<T>(v));
+ void setHeight(T v) {
+ height = clamp<int32_t>(v);
}
- // ------------------------------------------------------------------------
- // Assignment
- // ------------------------------------------------------------------------
+ void set(Size size) { *this = size; }
- void set(const Size& size) { *this = size; }
template <typename T>
- void set(T&& w, T&& h) {
- set(Size(std::forward<T>(w), std::forward<T>(h)));
+ void set(T w, T h) {
+ set(Size(w, h));
}
- // Sets the value to INVALID
- void makeInvalid() { set(INVALID); }
+ // Sets the value to kInvalidSize
+ void makeInvalid();
- // Sets the value to EMPTY
- void clear() { set(EMPTY); }
+ // Sets the value to kEmptySize
+ void clear();
- // ------------------------------------------------------------------------
- // Semantic checks
- // ------------------------------------------------------------------------
-
- // Valid means non-negative width and height
- bool isValid() const { return width >= 0 && height >= 0; }
-
- // Empty means zero width and height
- bool isEmpty() const { return *this == EMPTY; }
-
- // ------------------------------------------------------------------------
- // Clamp Helpers
- // ------------------------------------------------------------------------
-
- // Note: We use only features available in C++11 here for compatibility with
- // external targets which include this file directly or indirectly and which
- // themselves use C++11.
-
- // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
+ // TODO: Replace with std::remove_cvref_t in C++20.
template <typename T>
- using remove_cv_reference_t =
- typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+ using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
// Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
// clamping the input value to the output range if necessary.
template <typename ToType, typename FromType>
- static Size::remove_cv_reference_t<ToType>
- clamp(typename std::enable_if<
- std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
- std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
- FromType>::type v) {
- using BareToType = remove_cv_reference_t<ToType>;
- using BareFromType = remove_cv_reference_t<FromType>;
- static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
- static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
- static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
- static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
+ static constexpr remove_cvref_t<ToType> clamp(FromType v) {
+ using BareToType = remove_cvref_t<ToType>;
+ using ToLimits = std::numeric_limits<BareToType>;
+
+ using BareFromType = remove_cvref_t<FromType>;
+ using FromLimits = std::numeric_limits<BareFromType>;
+
+ static_assert(ToLimits::is_specialized && FromLimits::is_specialized);
+
+ constexpr auto toHighest = ToLimits::max();
+ constexpr auto toLowest = ToLimits::lowest();
+ constexpr auto fromHighest = FromLimits::max();
+ constexpr auto fromLowest = FromLimits::lowest();
// Get the closest representation of [toLowest, toHighest] in type
// FromType to use to clamp the input value before conversion.
@@ -127,37 +94,35 @@ struct Size {
// std::common_type<...> is used to get a value-preserving type for the
// top end of the range.
using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+ using CommonLimits = std::numeric_limits<CommonHighestType>;
// std::make_signed<std::common_type<...>> is used to get a
// value-preserving type for the bottom end of the range, except this is
// a bit trickier for non-integer types like float.
- using CommonLowestType =
- std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
- std::make_signed_t<std::conditional_t<
- std::numeric_limits<CommonHighestType>::is_integer,
- CommonHighestType, int /* not used */>>,
- CommonHighestType>;
+ using CommonLowestType = std::conditional_t<
+ CommonLimits::is_integer,
+ std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType,
+ int /* not used */>>,
+ CommonHighestType>;
// We can then compute the clamp range in a way that can be later
// trivially converted to either the 'from' or 'to' types, and be
- // representabile in either.
- static constexpr auto commonClampHighest =
- std::min(static_cast<CommonHighestType>(fromHighest),
- static_cast<CommonHighestType>(toHighest));
- static constexpr auto commonClampLowest =
- std::max(static_cast<CommonLowestType>(fromLowest),
- static_cast<CommonLowestType>(toLowest));
+ // representable in either.
+ constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest),
+ static_cast<CommonHighestType>(toHighest));
+ constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest),
+ static_cast<CommonLowestType>(toLowest));
- static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
- static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+ constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+ constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
// A clamp is needed only if the range we are clamping to is not the
// same as the range of the input.
- static constexpr bool isClampNeeded =
+ constexpr bool isClampNeeded =
(fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
// If a clamp is not needed, the conversion is just a trivial cast.
- if (!isClampNeeded) {
+ if constexpr (!isClampNeeded) {
return static_cast<BareToType>(v);
}
@@ -170,34 +135,46 @@ struct Size {
// Otherwise clamping is done by using the already computed endpoints
// for each type.
- return (v <= fromClampLowest)
- ? toClampLowest
- : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
+ if (v <= fromClampLowest) {
+ return toClampLowest;
+ }
+
+ return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v);
}
};
-// ------------------------------------------------------------------------
-// Comparisons
-// ------------------------------------------------------------------------
+constexpr Size kInvalidSize;
+constexpr Size kEmptySize{0, 0};
+
+inline void Size::makeInvalid() {
+ set(kInvalidSize);
+}
-inline bool operator==(const Size& lhs, const Size& rhs) {
+inline void Size::clear() {
+ set(kEmptySize);
+}
+
+inline bool operator==(Size lhs, Size rhs) {
return lhs.width == rhs.width && lhs.height == rhs.height;
}
-inline bool operator!=(const Size& lhs, const Size& rhs) {
- return !operator==(lhs, rhs);
+inline bool Size::isEmpty() const {
+ return *this == kEmptySize;
+}
+
+inline bool operator!=(Size lhs, Size rhs) {
+ return !(lhs == rhs);
}
-inline bool operator<(const Size& lhs, const Size& rhs) {
+inline bool operator<(Size lhs, Size rhs) {
// Orders by increasing width, then height.
if (lhs.width != rhs.width) return lhs.width < rhs.width;
return lhs.height < rhs.height;
}
// Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Size& size, ::std::ostream* os) {
- *os << "Size(" << size.width << ", " << size.height << ")";
+inline void PrintTo(Size size, std::ostream* stream) {
+ *stream << "Size(" << size.width << ", " << size.height << ')';
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h
index e86ca29a2a..cc7c869b3b 100644
--- a/libs/ui/include/ui/StaticDisplayInfo.h
+++ b/libs/ui/include/ui/StaticDisplayInfo.h
@@ -19,6 +19,7 @@
#include <optional>
#include <ui/DeviceProductInfo.h>
+#include <ui/Rotation.h>
#include <utils/Flattenable.h>
namespace android::ui {
@@ -31,6 +32,7 @@ struct StaticDisplayInfo : LightFlattenable<StaticDisplayInfo> {
float density = 0.f;
bool secure = false;
std::optional<DeviceProductInfo> deviceProductInfo;
+ Rotation installOrientation = ROTATION_0;
bool isFixedSize() const { return false; }
size_t getFlattenedSize() const;
diff --git a/libs/ui/include/ui/fuzzer/FuzzableDataspaces.h b/libs/ui/include/ui/fuzzer/FuzzableDataspaces.h
new file mode 100644
index 0000000000..4200d6a72d
--- /dev/null
+++ b/libs/ui/include/ui/fuzzer/FuzzableDataspaces.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/GraphicTypes.h>
+using namespace android;
+
+constexpr ui::Dataspace kDataspaces[] = {
+ ui::Dataspace::UNKNOWN,
+ ui::Dataspace::ARBITRARY,
+ ui::Dataspace::STANDARD_UNSPECIFIED,
+ ui::Dataspace::STANDARD_BT709,
+ ui::Dataspace::STANDARD_BT601_625,
+ ui::Dataspace::STANDARD_BT601_625_UNADJUSTED,
+ ui::Dataspace::STANDARD_BT601_525,
+ ui::Dataspace::STANDARD_BT601_525_UNADJUSTED,
+ ui::Dataspace::STANDARD_BT2020,
+ ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE,
+ ui::Dataspace::STANDARD_BT470M,
+ ui::Dataspace::STANDARD_FILM,
+ ui::Dataspace::STANDARD_DCI_P3,
+ ui::Dataspace::STANDARD_ADOBE_RGB,
+ ui::Dataspace::TRANSFER_UNSPECIFIED,
+ ui::Dataspace::TRANSFER_LINEAR,
+ ui::Dataspace::TRANSFER_SRGB,
+ ui::Dataspace::TRANSFER_SMPTE_170M,
+ ui::Dataspace::TRANSFER_GAMMA2_2,
+ ui::Dataspace::TRANSFER_GAMMA2_6,
+ ui::Dataspace::TRANSFER_GAMMA2_8,
+ ui::Dataspace::TRANSFER_ST2084,
+ ui::Dataspace::TRANSFER_HLG,
+ ui::Dataspace::RANGE_UNSPECIFIED,
+ ui::Dataspace::RANGE_FULL,
+ ui::Dataspace::RANGE_LIMITED,
+ ui::Dataspace::RANGE_EXTENDED,
+ ui::Dataspace::SRGB_LINEAR,
+ ui::Dataspace::V0_SRGB_LINEAR,
+ ui::Dataspace::V0_SCRGB_LINEAR,
+ ui::Dataspace::SRGB,
+ ui::Dataspace::V0_SRGB,
+ ui::Dataspace::V0_SCRGB,
+ ui::Dataspace::JFIF,
+ ui::Dataspace::V0_JFIF,
+ ui::Dataspace::BT601_625,
+ ui::Dataspace::V0_BT601_625,
+ ui::Dataspace::BT601_525,
+ ui::Dataspace::V0_BT601_525,
+ ui::Dataspace::BT709,
+ ui::Dataspace::V0_BT709,
+ ui::Dataspace::DCI_P3_LINEAR,
+ ui::Dataspace::DCI_P3,
+ ui::Dataspace::DISPLAY_P3_LINEAR,
+ ui::Dataspace::DISPLAY_P3,
+ ui::Dataspace::ADOBE_RGB,
+ ui::Dataspace::BT2020_LINEAR,
+ ui::Dataspace::BT2020,
+ ui::Dataspace::BT2020_PQ,
+ ui::Dataspace::DEPTH,
+ ui::Dataspace::SENSOR,
+ ui::Dataspace::BT2020_ITU,
+ ui::Dataspace::BT2020_ITU_PQ,
+ ui::Dataspace::BT2020_ITU_HLG,
+ ui::Dataspace::BT2020_HLG,
+ ui::Dataspace::DISPLAY_BT2020,
+ ui::Dataspace::DYNAMIC_DEPTH,
+ ui::Dataspace::JPEG_APP_SEGMENTS,
+ ui::Dataspace::HEIF,
+};
diff --git a/libs/ui/include_mock/ui/MockFence.h b/libs/ui/include_mock/ui/MockFence.h
new file mode 100644
index 0000000000..71adee4fbc
--- /dev/null
+++ b/libs/ui/include_mock/ui/MockFence.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+#include <ui/Fence.h>
+
+namespace android::mock {
+
+class MockFence : public android::Fence {
+public:
+ MockFence() = default;
+ virtual ~MockFence() = default;
+
+ MOCK_METHOD(nsecs_t, getSignalTime, (), (const, override));
+ MOCK_METHOD(Status, getStatus, (), (override));
+};
+
+}; // namespace android::mock
diff --git a/libs/ui/include_types/ui/DataspaceUtils.h b/libs/ui/include_types/ui/DataspaceUtils.h
new file mode 100644
index 0000000000..a461cb4e68
--- /dev/null
+++ b/libs/ui/include_types/ui/DataspaceUtils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/GraphicTypes.h>
+
+namespace android {
+
+inline bool isHdrDataspace(ui::Dataspace dataspace) {
+ const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+ return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
+} // namespace android \ No newline at end of file
diff --git a/libs/ui/include_types/ui/GraphicTypes.h b/libs/ui/include_types/ui/GraphicTypes.h
new file mode 120000
index 0000000000..b1859e0f51
--- /dev/null
+++ b/libs/ui/include_types/ui/GraphicTypes.h
@@ -0,0 +1 @@
+../../include/ui/GraphicTypes.h \ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 516aad824e..22fbf45c8c 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -27,28 +27,40 @@ cc_test {
name: "Region_test",
shared_libs: ["libui"],
srcs: ["Region_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "colorspace_test",
shared_libs: ["libui"],
srcs: ["colorspace_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "DisplayId_test",
shared_libs: ["libui"],
srcs: ["DisplayId_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "FlattenableHelpers_test",
shared_libs: ["libui"],
srcs: ["FlattenableHelpers_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -68,7 +80,10 @@ cc_test {
"GraphicBufferAllocator_test.cpp",
"mock/MockGrallocAllocator.cpp",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -83,14 +98,20 @@ cc_test {
"libutils",
],
srcs: ["GraphicBuffer_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
// This test has a main method, and requires a separate binary to be built.
cc_test {
name: "GraphicBufferOverBinder_test",
srcs: ["GraphicBufferOverBinder_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbinder",
"libgui",
@@ -105,7 +126,10 @@ cc_test {
test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Rect_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -113,5 +137,39 @@ cc_test {
test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Size_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "MockFence_test",
+ shared_libs: ["libui"],
+ static_libs: ["libgmock"],
+ srcs: ["MockFence_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "Transform_test",
+ shared_libs: ["libui"],
+ srcs: ["Transform_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "DataspaceUtils_test",
+ shared_libs: ["libui"],
+ srcs: ["DataspaceUtils_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp
new file mode 100644
index 0000000000..3e0967182b
--- /dev/null
+++ b/libs/ui/tests/DataspaceUtils_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "DataspaceUtilsTest"
+
+#include <gtest/gtest.h>
+#include <ui/DataspaceUtils.h>
+
+namespace android {
+
+class DataspaceUtilsTest : public testing::Test {};
+
+TEST_F(DataspaceUtilsTest, isHdrDataspace) {
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_HLG));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG));
+
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR));
+ // scRGB defines a very wide gamut but not an expanded luminance range
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT709));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::ADOBE_RGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_ITU));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_BT2020));
+}
+
+} // namespace android
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
index 1d908b8ef1..8ddee7e740 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -32,6 +32,9 @@ TEST(DisplayIdTest, createPhysicalIdFromEdid) {
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
}
TEST(DisplayIdTest, createPhysicalIdFromPort) {
@@ -43,6 +46,9 @@ TEST(DisplayIdTest, createPhysicalIdFromPort) {
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
}
TEST(DisplayIdTest, createGpuVirtualId) {
@@ -52,6 +58,9 @@ TEST(DisplayIdTest, createGpuVirtualId) {
EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
}
TEST(DisplayIdTest, createHalVirtualId) {
@@ -61,6 +70,9 @@ TEST(DisplayIdTest, createHalVirtualId) {
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));
}
} // namespace android::ui
diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
new file mode 100644
index 0000000000..736979a7c5
--- /dev/null
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <functional>
+#include <string_view>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <ui/DisplayIdentification.h>
+
+using ::testing::ElementsAre;
+
+namespace android {
+
+namespace {
+
+const unsigned char kInternalEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
+ "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
+ "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
+ "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
+ "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
+ "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
+
+const unsigned char kExternalEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01"
+ "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
+ "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
+ "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
+ "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
+ "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff"
+ "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71";
+
+// Extended EDID with timing extension.
+const unsigned char kExternalEedid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
+ "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
+ "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+ "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+ "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30"
+ "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+ "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+ "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d"
+ "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07"
+ "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01"
+ "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00"
+ "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00"
+ "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0"
+ "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
+
+const unsigned char kPanasonicTvEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x34\xa9\x96\xa2\x01\x01\x01"
+ "\x01\x00\x1d\x01\x03\x80\x80\x48\x78\x0a\xda\xff\xa3\x58\x4a"
+ "\xa2\x29\x17\x49\x4b\x20\x08\x00\x31\x40\x61\x40\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x08\xe8\x00\x30\xf2\x70"
+ "\x5a\x80\xb0\x58\x8a\x00\xba\x88\x21\x00\x00\x1e\x02\x3a\x80"
+ "\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\xba\x88\x21\x00\x00\x1e"
+ "\x00\x00\x00\xfc\x00\x50\x61\x6e\x61\x73\x6f\x6e\x69\x63\x2d"
+ "\x54\x56\x0a\x00\x00\x00\xfd\x00\x17\x3d\x0f\x88\x3c\x00\x0a"
+ "\x20\x20\x20\x20\x20\x20\x01\x1d\x02\x03\x6b\xf0\x57\x61\x60"
+ "\x10\x1f\x66\x65\x05\x14\x20\x21\x22\x04\x13\x03\x12\x07\x16"
+ "\x5d\x5e\x5f\x62\x63\x64\x2c\x0d\x07\x01\x15\x07\x50\x57\x07"
+ "\x01\x67\x04\x03\x83\x0f\x00\x00\x6e\x03\x0c\x00\x20\x00\x38"
+ "\x3c\x2f\x08\x80\x01\x02\x03\x04\x67\xd8\x5d\xc4\x01\x78\x80"
+ "\x03\xe2\x00\x4b\xe3\x05\xff\x01\xe2\x0f\x33\xe3\x06\x0f\x01"
+ "\xe5\x01\x8b\x84\x90\x01\xeb\x01\x46\xd0\x00\x44\x03\x70\x80"
+ "\x5e\x75\x94\xe6\x11\x46\xd0\x00\x70\x00\x66\x21\x56\xaa\x51"
+ "\x00\x1e\x30\x46\x8f\x33\x00\xba\x88\x21\x00\x00\x1e\x00\x00"
+ "\xc8";
+
+const unsigned char kHisenseTvEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x20\xa3\x00\x00\x00\x00\x00"
+ "\x00\x12\x1d\x01\x03\x80\x00\x00\x78\x0a\xd7\xa5\xa2\x59\x4a"
+ "\x96\x24\x14\x50\x54\xa3\x08\x00\xd1\xc0\xb3\x00\x81\x00\x81"
+ "\x80\x81\x40\x81\xc0\x01\x01\x01\x01\x02\x3a\x80\x18\x71\x38"
+ "\x2d\x40\x58\x2c\x45\x00\x3f\x43\x21\x00\x00\x1a\x02\x3a\x80"
+ "\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\x3f\x43\x21\x00\x00\x1a"
+ "\x00\x00\x00\xfd\x00\x1e\x4c\x1e\x5a\x1e\x00\x0a\x20\x20\x20"
+ "\x20\x20\x20\x00\x00\x00\xfc\x00\x48\x69\x73\x65\x6e\x73\x65"
+ "\x0a\x20\x20\x20\x20\x20\x01\x47\x02\x03\x2d\x71\x50\x90\x05"
+ "\x04\x03\x07\x02\x06\x01\x1f\x14\x13\x12\x16\x11\x15\x20\x2c"
+ "\x09\x07\x03\x15\x07\x50\x57\x07\x00\x39\x07\xbb\x66\x03\x0c"
+ "\x00\x12\x34\x00\x83\x01\x00\x00\x01\x1d\x00\x72\x51\xd0\x1e"
+ "\x20\x6e\x28\x55\x00\xc4\x8e\x21\x00\x00\x1e\x01\x1d\x80\x18"
+ "\x71\x1c\x16\x20\x58\x2c\x25\x00\xc4\x8e\x21\x00\x00\x9e\x8c"
+ "\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\x13\x8e\x21\x00"
+ "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x07";
+
+const unsigned char kCtlDisplayEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x00\x00\x00\x00"
+ "\xff\x17\x01\x04\xa5\x34\x1d\x78\x3a\xa7\x25\xa4\x57\x51\xa0\x26"
+ "\x10\x50\x54\xbf\xef\x80\xb3\x00\xa9\x40\x95\x00\x81\x40\x81\x80"
+ "\x95\x0f\x71\x4f\x90\x40\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+ "\x45\x00\x09\x25\x21\x00\x00\x1e\x66\x21\x50\xb0\x51\x00\x1b\x30"
+ "\x40\x70\x36\x00\x09\x25\x21\x00\x00\x1e\x00\x00\x00\xfd\x00\x31"
+ "\x4c\x1e\x52\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfc"
+ "\x00\x4c\x50\x32\x33\x36\x31\x0a\x20\x20\x20\x20\x20\x20\x01\x3e"
+ "\x02\x03\x22\xf2\x4f\x90\x9f\x05\x14\x04\x13\x03\x02\x12\x11\x07"
+ "\x06\x16\x15\x01\x23\x09\x07\x07\x83\x01\x00\x00\x65\xb9\x14\x00"
+ "\x04\x00\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\x09\x25"
+ "\x21\x00\x00\x1e\x02\x3a\x80\xd0\x72\x38\x2d\x40\x10\x2c\x45\x80"
+ "\x09\x25\x21\x00\x00\x1e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28"
+ "\x55\x00\x09\x25\x21\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10"
+ "\x10\x3e\x96\x00\x09\x25\x21\x00\x00\x18\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4";
+
+template <size_t N>
+DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) {
+ return DisplayIdentificationData(bytes, bytes + N - 1);
+}
+
+uint32_t hash(const char* str) {
+ return static_cast<uint32_t>(cityHash64Len0To16(str));
+}
+
+} // namespace
+
+const DisplayIdentificationData& getInternalEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kInternalEdid);
+ return data;
+}
+
+const DisplayIdentificationData& getExternalEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEdid);
+ return data;
+}
+
+const DisplayIdentificationData& getExternalEedid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEedid);
+ return data;
+}
+
+const DisplayIdentificationData& getPanasonicTvEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kPanasonicTvEdid);
+ return data;
+}
+
+const DisplayIdentificationData& getHisenseTvEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kHisenseTvEdid);
+ return data;
+}
+
+const DisplayIdentificationData& getCtlDisplayEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kCtlDisplayEdid);
+ return data;
+}
+
+TEST(DisplayIdentificationTest, isEdid) {
+ EXPECT_FALSE(isEdid({}));
+
+ EXPECT_TRUE(isEdid(getInternalEdid()));
+ EXPECT_TRUE(isEdid(getExternalEdid()));
+ EXPECT_TRUE(isEdid(getExternalEedid()));
+ EXPECT_TRUE(isEdid(getPanasonicTvEdid()));
+ EXPECT_TRUE(isEdid(getHisenseTvEdid()));
+ EXPECT_TRUE(isEdid(getCtlDisplayEdid()));
+}
+
+TEST(DisplayIdentificationTest, parseEdid) {
+ auto edid = parseEdid(getInternalEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(0x4ca3u, edid->manufacturerId);
+ EXPECT_STREQ("SEC", edid->pnpId.data());
+ // ASCII text should be used as fallback if display name and serial number are missing.
+ EXPECT_EQ(hash("121AT11-801"), edid->modelHash);
+ EXPECT_TRUE(edid->displayName.empty());
+ EXPECT_EQ(12610, edid->productId);
+ EXPECT_EQ(21, edid->manufactureOrModelYear);
+ EXPECT_EQ(0, edid->manufactureWeek);
+ EXPECT_FALSE(edid->cea861Block);
+
+ edid = parseEdid(getExternalEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(0x22f0u, edid->manufacturerId);
+ EXPECT_STREQ("HWP", edid->pnpId.data());
+ EXPECT_EQ(hash("HP ZR30w"), edid->modelHash);
+ EXPECT_EQ("HP ZR30w", edid->displayName);
+ EXPECT_EQ(10348, edid->productId);
+ EXPECT_EQ(22, edid->manufactureOrModelYear);
+ EXPECT_EQ(2, edid->manufactureWeek);
+ EXPECT_FALSE(edid->cea861Block);
+
+ edid = parseEdid(getExternalEedid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(0x4c2du, edid->manufacturerId);
+ EXPECT_STREQ("SAM", edid->pnpId.data());
+ EXPECT_EQ(hash("SAMSUNG"), edid->modelHash);
+ EXPECT_EQ("SAMSUNG", edid->displayName);
+ EXPECT_EQ(2302, edid->productId);
+ EXPECT_EQ(21, edid->manufactureOrModelYear);
+ EXPECT_EQ(41, edid->manufactureWeek);
+ ASSERT_TRUE(edid->cea861Block);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+ auto physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+ EXPECT_EQ(2, physicalAddress.a);
+ EXPECT_EQ(0, physicalAddress.b);
+ EXPECT_EQ(0, physicalAddress.c);
+ EXPECT_EQ(0, physicalAddress.d);
+
+ edid = parseEdid(getPanasonicTvEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(13481, edid->manufacturerId);
+ EXPECT_STREQ("MEI", edid->pnpId.data());
+ EXPECT_EQ(hash("Panasonic-TV"), edid->modelHash);
+ EXPECT_EQ("Panasonic-TV", edid->displayName);
+ EXPECT_EQ(41622, edid->productId);
+ EXPECT_EQ(29, edid->manufactureOrModelYear);
+ EXPECT_EQ(0, edid->manufactureWeek);
+ ASSERT_TRUE(edid->cea861Block);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+ physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+ EXPECT_EQ(2, physicalAddress.a);
+ EXPECT_EQ(0, physicalAddress.b);
+ EXPECT_EQ(0, physicalAddress.c);
+ EXPECT_EQ(0, physicalAddress.d);
+
+ edid = parseEdid(getHisenseTvEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(8355, edid->manufacturerId);
+ EXPECT_STREQ("HEC", edid->pnpId.data());
+ EXPECT_EQ(hash("Hisense"), edid->modelHash);
+ EXPECT_EQ("Hisense", edid->displayName);
+ EXPECT_EQ(0, edid->productId);
+ EXPECT_EQ(29, edid->manufactureOrModelYear);
+ EXPECT_EQ(18, edid->manufactureWeek);
+ ASSERT_TRUE(edid->cea861Block);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+ physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+ EXPECT_EQ(1, physicalAddress.a);
+ EXPECT_EQ(2, physicalAddress.b);
+ EXPECT_EQ(3, physicalAddress.c);
+ EXPECT_EQ(4, physicalAddress.d);
+
+ edid = parseEdid(getCtlDisplayEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(3724, edid->manufacturerId);
+ EXPECT_STREQ("CTL", edid->pnpId.data());
+ EXPECT_EQ(hash("LP2361"), edid->modelHash);
+ EXPECT_EQ("LP2361", edid->displayName);
+ EXPECT_EQ(9373, edid->productId);
+ EXPECT_EQ(23, edid->manufactureOrModelYear);
+ EXPECT_EQ(0xff, edid->manufactureWeek);
+ ASSERT_TRUE(edid->cea861Block);
+ EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock);
+}
+
+TEST(DisplayIdentificationTest, parseInvalidEdid) {
+ EXPECT_FALSE(isEdid({}));
+ EXPECT_FALSE(parseEdid({}));
+
+ // Display name must be printable.
+ auto data = getExternalEdid();
+ data[97] = '\x1b';
+ auto edid = parseEdid(data);
+ ASSERT_TRUE(edid);
+ // Serial number should be used as fallback if display name is invalid.
+ const auto modelHash = hash("CN4202137Q");
+ EXPECT_EQ(modelHash, edid->modelHash);
+ EXPECT_TRUE(edid->displayName.empty());
+
+ // Parsing should succeed even if EDID is truncated.
+ data.pop_back();
+ edid = parseEdid(data);
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(modelHash, edid->modelHash);
+}
+
+TEST(DisplayIdentificationTest, getPnpId) {
+ EXPECT_FALSE(getPnpId(0));
+ EXPECT_FALSE(getPnpId(static_cast<uint16_t>(-1)));
+
+ EXPECT_STREQ("SEC", getPnpId(0x4ca3u).value_or(PnpId{}).data());
+ EXPECT_STREQ("HWP", getPnpId(0x22f0u).value_or(PnpId{}).data());
+ EXPECT_STREQ("SAM", getPnpId(0x4c2du).value_or(PnpId{}).data());
+}
+
+TEST(DisplayIdentificationTest, parseDisplayIdentificationData) {
+ const auto primaryInfo = parseDisplayIdentificationData(0, getInternalEdid());
+ ASSERT_TRUE(primaryInfo);
+
+ const auto secondaryInfo = parseDisplayIdentificationData(1, getExternalEdid());
+ ASSERT_TRUE(secondaryInfo);
+
+ const auto tertiaryInfo = parseDisplayIdentificationData(2, getExternalEedid());
+ ASSERT_TRUE(tertiaryInfo);
+
+ // Display IDs should be unique.
+ EXPECT_EQ(4633257497453176576, primaryInfo->id.value);
+ EXPECT_EQ(4621520285560261121, secondaryInfo->id.value);
+ EXPECT_EQ(4633127902230889474, tertiaryInfo->id.value);
+}
+
+TEST(DisplayIdentificationTest, deviceProductInfo) {
+ using ManufactureYear = DeviceProductInfo::ManufactureYear;
+ using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;
+ using ModelYear = DeviceProductInfo::ModelYear;
+
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_EQ("", info.name);
+ EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
+ EXPECT_EQ("12610", info.productId);
+ ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
+ EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year);
+ EXPECT_TRUE(info.relativeAddress.empty());
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_EQ("HP ZR30w", info.name);
+ EXPECT_STREQ("HWP", info.manufacturerPnpId.data());
+ EXPECT_EQ("10348", info.productId);
+ ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+ const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+ EXPECT_EQ(2012, date.year);
+ EXPECT_EQ(2, date.week);
+ EXPECT_TRUE(info.relativeAddress.empty());
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_EQ("SAMSUNG", info.name);
+ EXPECT_STREQ("SAM", info.manufacturerPnpId.data());
+ EXPECT_EQ("2302", info.productId);
+ ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+ const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+ EXPECT_EQ(2011, date.year);
+ EXPECT_EQ(41, date.week);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_EQ("Panasonic-TV", info.name);
+ EXPECT_STREQ("MEI", info.manufacturerPnpId.data());
+ EXPECT_EQ("41622", info.productId);
+ ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
+ const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate);
+ EXPECT_EQ(2019, date.year);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_EQ("Hisense", info.name);
+ EXPECT_STREQ("HEC", info.manufacturerPnpId.data());
+ EXPECT_EQ("0", info.productId);
+ ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+ const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+ EXPECT_EQ(2019, date.year);
+ EXPECT_EQ(18, date.week);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(1, 2, 3, 4));
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_EQ("LP2361", info.name);
+ EXPECT_STREQ("CTL", info.manufacturerPnpId.data());
+ EXPECT_EQ("9373", info.productId);
+ ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate));
+ EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year);
+ EXPECT_TRUE(info.relativeAddress.empty());
+ }
+}
+
+TEST(DisplayIdentificationTest, fromPort) {
+ // Manufacturer ID should be invalid.
+ ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0)));
+ ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0xffu)));
+}
+
+TEST(DisplayIdentificationTest, getVirtualDisplayId) {
+ // Manufacturer ID should be invalid.
+ ASSERT_FALSE(getPnpId(getVirtualDisplayId(0)));
+ ASSERT_FALSE(getPnpId(getVirtualDisplayId(0xffff'ffffu)));
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra" \ No newline at end of file
diff --git a/libs/ui/tests/MockFence_test.cpp b/libs/ui/tests/MockFence_test.cpp
new file mode 100644
index 0000000000..40dddc3cf2
--- /dev/null
+++ b/libs/ui/tests/MockFence_test.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/MockFence.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+using testing::Return;
+
+class MockFenceTest : public testing::Test {
+public:
+ sp<Fence> getFenceForTesting() const { return mMockFence; }
+
+ const mock::MockFence& getMockFence() const { return *mMockFence; }
+
+private:
+ sp<mock::MockFence> mMockFence = sp<mock::MockFence>::make();
+};
+
+TEST_F(MockFenceTest, getSignalTime) {
+ sp<Fence> fence = getFenceForTesting();
+
+ EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(Fence::SIGNAL_TIME_PENDING));
+ EXPECT_EQ(Fence::SIGNAL_TIME_PENDING, fence->getSignalTime());
+
+ EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(1234));
+ EXPECT_EQ(1234, fence->getSignalTime());
+}
+
+TEST_F(MockFenceTest, getStatus) {
+ sp<Fence> fence = getFenceForTesting();
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Unsignaled));
+ EXPECT_EQ(Fence::Status::Unsignaled, fence->getStatus());
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Signaled));
+ EXPECT_EQ(Fence::Status::Signaled, fence->getStatus());
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Invalid));
+ EXPECT_EQ(Fence::Status::Invalid, fence->getStatus());
+}
+} // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 5f75aeabeb..acef47fb97 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -93,9 +93,8 @@ TEST(SizeTest, ValidAndEmpty) {
}
{
- const auto& s = Size::INVALID;
- EXPECT_FALSE(s.isValid());
- EXPECT_FALSE(s.isEmpty());
+ EXPECT_FALSE(kInvalidSize.isValid());
+ EXPECT_FALSE(kInvalidSize.isEmpty());
}
{
@@ -112,9 +111,8 @@ TEST(SizeTest, ValidAndEmpty) {
}
{
- const auto& s = Size::EMPTY;
- EXPECT_TRUE(s.isValid());
- EXPECT_TRUE(s.isEmpty());
+ EXPECT_TRUE(kEmptySize.isValid());
+ EXPECT_TRUE(kEmptySize.isEmpty());
}
{
diff --git a/libs/ui/tests/Transform_test.cpp b/libs/ui/tests/Transform_test.cpp
new file mode 100644
index 0000000000..6964284eaa
--- /dev/null
+++ b/libs/ui/tests/Transform_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/Transform.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(TransformTest, inverseRotation_hasCorrectType) {
+ const auto testRotationFlagsForInverse = [](Transform::RotationFlags rotation,
+ Transform::RotationFlags expectedInverse,
+ bool isRotation) {
+ const Transform t(rotation, 0, 0);
+ EXPECT_EQ(t.getOrientation(), rotation);
+ const Transform inverse = t.inverse();
+ EXPECT_EQ(inverse.getOrientation(), expectedInverse);
+
+ if (isRotation) {
+ EXPECT_TRUE(t.getType() & Transform::ROTATE);
+ EXPECT_TRUE(inverse.getType() & Transform::ROTATE);
+ } else {
+ EXPECT_FALSE(t.getType() & Transform::ROTATE);
+ EXPECT_FALSE(inverse.getType() & Transform::ROTATE);
+ }
+ };
+
+ testRotationFlagsForInverse(Transform::ROT_0, Transform::ROT_0, false);
+ testRotationFlagsForInverse(Transform::ROT_90, Transform::ROT_270, true);
+ testRotationFlagsForInverse(Transform::ROT_180, Transform::ROT_180, true);
+ testRotationFlagsForInverse(Transform::ROT_270, Transform::ROT_90, true);
+ testRotationFlagsForInverse(Transform::FLIP_H, Transform::FLIP_H, false);
+ testRotationFlagsForInverse(Transform::FLIP_V, Transform::FLIP_V, false);
+}
+
+} // namespace android::ui
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
deleted file mode 100644
index bf848af262..0000000000
--- a/libs/vr/libvrflinger/Android.bp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
- "acquired_buffer.cpp",
- "epoll_event_dispatcher.cpp",
- "display_manager_service.cpp",
- "display_service.cpp",
- "display_surface.cpp",
- "hardware_composer.cpp",
- "vr_flinger.cpp",
-]
-
-includeFiles = [ "include" ]
-
-staticLibraries = [
- "libdisplay",
- "libdvrcommon",
- "libperformance",
- "libvrsensor",
- "libbroadcastring",
- "libvr_manager",
- "libbroadcastring",
-]
-
-sharedLibraries = [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.composer@2.2",
- "android.hardware.graphics.composer@2.3",
- "android.hardware.graphics.composer@2.4",
- "libbinder",
- "libbase",
- "libbufferhubqueue",
- "libcutils",
- "liblog",
- "libhardware",
- "libnativewindow",
- "libprocessgroup",
- "libutils",
- "libEGL",
- "libGLESv1_CM",
- "libGLESv2",
- "libvulkan",
- "libui",
- "libgui",
- "libsync",
- "libhidlbase",
- "libfmq",
- "libpdx_default_transport",
-]
-
-headerLibraries = [
- "android.hardware.graphics.composer@2.1-command-buffer",
- "android.hardware.graphics.composer@2.2-command-buffer",
- "android.hardware.graphics.composer@2.3-command-buffer",
- "android.hardware.graphics.composer@2.4-command-buffer",
- "libdvr_headers",
- "libsurfaceflinger_headers",
-]
-
-cc_library_static {
- srcs: sourceFiles,
- export_include_dirs: includeFiles,
-
- clang: true,
- cflags: [
- "-DLOG_TAG=\"vr_flinger\"",
- "-DTRACE=0",
- "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
- "-DGL_GLEXT_PROTOTYPES",
- "-DEGL_EGLEXT_PROTOTYPES",
- "-Wall",
- "-Werror",
- "-Wno-error=sign-compare", // to fix later
- "-Wno-unused-variable",
- ],
- shared_libs: sharedLibraries,
- whole_static_libs: staticLibraries,
- header_libs: headerLibraries,
- name: "libvrflinger",
-}
-
-subdirs = [
- "tests",
-]
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
deleted file mode 100644
index c360deed5b..0000000000
--- a/libs/vr/libvrflinger/acquired_buffer.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#include "acquired_buffer.h"
-
-#include <log/log.h>
-#include <sync/sync.h>
-
-using android::pdx::LocalHandle;
-
-namespace android {
-namespace dvr {
-
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
- LocalHandle acquire_fence, std::size_t slot)
- : buffer_(buffer), acquire_fence_(std::move(acquire_fence)), slot_(slot) {}
-
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
- int* error) {
- LocalHandle fence;
- const int ret = buffer->Acquire(&fence);
-
- if (error)
- *error = ret;
-
- if (ret < 0) {
- ALOGW("AcquiredBuffer::AcquiredBuffer: Failed to acquire buffer: %s",
- strerror(-ret));
- buffer_ = nullptr;
- // Default construct sets acquire_fence_ to empty.
- } else {
- buffer_ = buffer;
- acquire_fence_ = std::move(fence);
- }
-}
-
-AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other) noexcept {
- *this = std::move(other);
-}
-
-AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); }
-
-AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) noexcept {
- if (this != &other) {
- Release();
-
- using std::swap;
- swap(buffer_, other.buffer_);
- swap(acquire_fence_, other.acquire_fence_);
- swap(slot_, other.slot_);
- }
- return *this;
-}
-
-bool AcquiredBuffer::IsAvailable() const {
- if (IsEmpty())
- return false;
-
- // Only check the fence if the acquire fence is not empty.
- if (acquire_fence_) {
- const int ret = sync_wait(acquire_fence_.Get(), 0);
- ALOGD_IF(TRACE || (ret < 0 && errno != ETIME),
- "AcquiredBuffer::IsAvailable: buffer_id=%d acquire_fence=%d "
- "sync_wait()=%d errno=%d.",
- buffer_->id(), acquire_fence_.Get(), ret, ret < 0 ? errno : 0);
- if (ret == 0) {
- // The fence is completed, so to avoid further calls to sync_wait we close
- // it here.
- acquire_fence_.Close();
- }
- return ret == 0;
- } else {
- return true;
- }
-}
-
-LocalHandle AcquiredBuffer::ClaimAcquireFence() {
- return std::move(acquire_fence_);
-}
-
-std::shared_ptr<ConsumerBuffer> AcquiredBuffer::ClaimBuffer() {
- return std::move(buffer_);
-}
-
-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_) {
- const int ret = buffer_->ReleaseAsync();
- if (ret < 0) {
- ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s",
- buffer_->id(), strerror(-ret));
- if (ret != -ESHUTDOWN)
- return ret;
- }
-
- buffer_ = nullptr;
- }
-
- acquire_fence_.Close();
- slot_ = 0;
- return 0;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
deleted file mode 100644
index 7643e75ecf..0000000000
--- a/libs/vr/libvrflinger/acquired_buffer.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
-
-#include <pdx/file_handle.h>
-#include <private/dvr/consumer_buffer.h>
-
-#include <memory>
-
-namespace android {
-namespace dvr {
-
-// Manages the ACQUIRE/RELEASE ownership cycle of a ConsumerBuffer.
-class AcquiredBuffer {
- public:
- static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle;
-
- AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {}
-
- // Constructs an AcquiredBuffer from a ConsumerBuffer pointer and an acquire
- // fence. The ConsumerBuffer MUST be in the ACQUIRED state prior to calling
- // this constructor; the constructor does not attempt to ACQUIRE the buffer
- // itself.
- AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
- pdx::LocalHandle acquire_fence, std::size_t slot = 0);
-
- // Constructs an AcquiredBuffer from a ConsumerBuffer. The ConsumerBuffer MUST
- // be in the POSTED state prior to calling this constructor, as this
- // constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails
- // this instance is left in the empty state. An optional error code is
- // returned in |error|, which may be nullptr if not needed.
- AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer, int* error);
-
- // Move constructor. Behaves similarly to the move assignment operator below.
- AcquiredBuffer(AcquiredBuffer&& other) noexcept;
-
- ~AcquiredBuffer();
-
- // Move assignment operator. Moves the ConsumerBuffer and acquire fence from
- // |other| into this instance after RELEASING the current ConsumerBuffer and
- // closing the acquire fence. After the move |other| is left in the empty
- // state.
- AcquiredBuffer& operator=(AcquiredBuffer&& other) noexcept;
-
- // Accessors for the underlying ConsumerBuffer, the acquire fence, and the
- // use-case specific sequence value from the acquisition (see
- // private/dvr/consumer_buffer.h).
- std::shared_ptr<ConsumerBuffer> buffer() const { return buffer_; }
- int acquire_fence() const { return acquire_fence_.Get(); }
-
- // When non-empty, returns true if the acquired fence was signaled (or if the
- // fence is empty). Returns false when empty or if the fence is not signaled.
- bool IsAvailable() const;
-
- bool IsEmpty() const { return buffer_ == nullptr; }
-
- // Returns the acquire fence, passing ownership to the caller.
- pdx::LocalHandle ClaimAcquireFence();
-
- // Returns the buffer, passing ownership to the caller. Caller is responsible
- // for calling Release on the returned buffer.
- std::shared_ptr<ConsumerBuffer> ClaimBuffer();
-
- // Releases the ConsumerBuffer, passing the release fence in |release_fence|
- // to the producer. On success, the ConsumerBuffer and acquire fence are set
- // to empty state; if release fails, the ConsumerBuffer and acquire fence are
- // left in place and a negative error code is returned.
- 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<ConsumerBuffer> 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;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
deleted file mode 100644
index 34b3b0a6f4..0000000000
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "display_manager_service.h"
-
-#include <pdx/channel_handle.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <private/android_filesystem_config.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/trusted_uids.h>
-#include <sys/poll.h>
-
-#include <array>
-
-using android::dvr::display::DisplayManagerProtocol;
-using android::pdx::Channel;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Message;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::ErrorStatus;
-using android::pdx::rpc::DispatchRemoteMethod;
-using android::pdx::rpc::IfAnyOf;
-using android::pdx::rpc::RemoteMethodError;
-
-namespace android {
-namespace dvr {
-
-void DisplayManager::SetNotificationsPending(bool pending) {
- auto status = service_->ModifyChannelEvents(channel_id_, pending ? 0 : POLLIN,
- pending ? POLLIN : 0);
- ALOGE_IF(!status,
- "DisplayManager::SetNotificationPending: Failed to modify channel "
- "events: %s",
- status.GetErrorMessage().c_str());
-}
-
-DisplayManagerService::DisplayManagerService(
- const std::shared_ptr<DisplayService>& display_service)
- : BASE("DisplayManagerService",
- Endpoint::Create(DisplayManagerProtocol::kClientPath)),
- display_service_(display_service) {
- display_service_->SetDisplayConfigurationUpdateNotifier(
- std::bind(&DisplayManagerService::OnDisplaySurfaceChange, this));
-}
-
-std::shared_ptr<pdx::Channel> DisplayManagerService::OnChannelOpen(
- pdx::Message& message) {
- const int user_id = message.GetEffectiveUserId();
- const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
-
- // Check if the display_manager_ has a defunct channel.
- if (display_manager_ && !HasChannelId(display_manager_->channel_id())) {
- ALOGE("DisplayManagerService::OnChannelOpen: Found defunct channel %d with "
- "no OnChannelClose, clearing prior display manager.",
- display_manager_->channel_id());
- display_manager_ = nullptr;
- }
-
- // Prevent more than one display manager from registering at a time or
- // untrusted UIDs from connecting.
- if (display_manager_ || !trusted) {
- RemoteMethodError(message, EPERM);
- return nullptr;
- }
-
- display_manager_ =
- std::make_shared<DisplayManager>(this, message.GetChannelId());
- return display_manager_;
-}
-
-void DisplayManagerService::OnChannelClose(
- pdx::Message& /*message*/, const std::shared_ptr<pdx::Channel>& channel) {
- // Unregister the display manager when the channel closes.
- if (display_manager_ == channel)
- display_manager_ = nullptr;
-}
-
-pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) {
- ATRACE_NAME("DisplayManagerService::HandleMessage");
- auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel());
-
- switch (message.GetOp()) {
- case DisplayManagerProtocol::GetSurfaceState::Opcode:
- DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceState>(
- *this, &DisplayManagerService::OnGetSurfaceState, message);
- return {};
-
- case DisplayManagerProtocol::GetSurfaceQueue::Opcode:
- DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
- *this, &DisplayManagerService::OnGetSurfaceQueue, message);
- return {};
-
- default:
- return Service::DefaultHandleMessage(message);
- }
-}
-
-pdx::Status<std::vector<display::SurfaceState>>
-DisplayManagerService::OnGetSurfaceState(pdx::Message& /*message*/) {
- std::vector<display::SurfaceState> items;
-
- display_service_->ForEachDisplaySurface(
- SurfaceType::Application,
- [&items](const std::shared_ptr<DisplaySurface>& surface) mutable {
- items.push_back({surface->surface_id(), surface->process_id(),
- surface->user_id(), surface->attributes(),
- surface->update_flags(), surface->GetQueueIds()});
- surface->ClearUpdate();
- });
-
- // The fact that we're in the message handler implies that display_manager_ is
- // not nullptr. No check required, unless this service becomes multi-threaded.
- display_manager_->SetNotificationsPending(false);
- return items;
-}
-
-pdx::Status<pdx::LocalChannelHandle> DisplayManagerService::OnGetSurfaceQueue(
- pdx::Message& /*message*/, int surface_id, int queue_id) {
- auto surface = display_service_->GetDisplaySurface(surface_id);
- if (!surface || surface->surface_type() != SurfaceType::Application)
- return ErrorStatus(EINVAL);
-
- auto queue =
- std::static_pointer_cast<ApplicationDisplaySurface>(surface)->GetQueue(
- queue_id);
- if (!queue)
- return ErrorStatus(EINVAL);
-
- auto status = queue->CreateConsumerQueueHandle();
- ALOGE_IF(
- !status,
- "DisplayManagerService::OnGetSurfaceQueue: Failed to create consumer "
- "queue for queue_id=%d: %s",
- queue->id(), status.GetErrorMessage().c_str());
-
- return status;
-}
-
-void DisplayManagerService::OnDisplaySurfaceChange() {
- if (display_manager_)
- display_manager_->SetNotificationsPending(true);
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
deleted file mode 100644
index 3133fe1884..0000000000
--- a/libs/vr/libvrflinger/display_manager_service.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
-#define ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
-
-#include <pdx/service.h>
-#include <pdx/status.h>
-#include <private/dvr/display_protocol.h>
-
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-class DisplayManagerService;
-
-// The display manager is a client of the display manager service. This class
-// represents the connected client that the display manager service sends
-// notifications to.
-class DisplayManager : public pdx::Channel {
- public:
- DisplayManager(DisplayManagerService* service, int channel_id)
- : service_(service), channel_id_(channel_id) {}
-
- int channel_id() const { return channel_id_; }
-
- // Sets or clears the channel event mask to indicate pending events that the
- // display manager on the other end of the channel should read and handle.
- // When |pending| is true the POLLIN bit is set in the event mask; when
- // |pending| is false the POLLIN bit is cleared in the event mask.
- void SetNotificationsPending(bool pending);
-
- private:
- DisplayManager(const DisplayManager&) = delete;
- void operator=(const DisplayManager&) = delete;
-
- DisplayManagerService* service_;
- int channel_id_;
-};
-
-// The display manager service marshalls state and events from the display
-// service to the display manager.
-class DisplayManagerService : public pdx::ServiceBase<DisplayManagerService> {
- public:
- std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
- void OnChannelClose(pdx::Message& message,
- const std::shared_ptr<pdx::Channel>& channel) override;
- pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
- private:
- friend BASE;
-
- explicit DisplayManagerService(
- const std::shared_ptr<DisplayService>& display_service);
-
- pdx::Status<std::vector<display::SurfaceState>> OnGetSurfaceState(
- pdx::Message& message);
- pdx::Status<pdx::LocalChannelHandle> OnGetSurfaceQueue(pdx::Message& message,
- int surface_id,
- int queue_id);
-
- // Called by the display service to indicate changes to display surfaces that
- // the display manager should evaluate.
- void OnDisplaySurfaceChange();
-
- DisplayManagerService(const DisplayManagerService&) = delete;
- void operator=(const DisplayManagerService&) = delete;
-
- std::shared_ptr<DisplayService> display_service_;
- std::shared_ptr<DisplayManager> display_manager_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
deleted file mode 100644
index 582fed3a4a..0000000000
--- a/libs/vr/libvrflinger/display_service.cpp
+++ /dev/null
@@ -1,437 +0,0 @@
-#include "display_service.h"
-
-#include <unistd.h>
-
-#include <algorithm>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <dvr/dvr_display_types.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <pdx/rpc/remote_method.h>
-#include <private/android_filesystem_config.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/numeric.h>
-#include <private/dvr/trusted_uids.h>
-#include <private/dvr/types.h>
-
-#include "DisplayHardware/DisplayIdentification.h"
-
-using android::dvr::display::DisplayProtocol;
-using android::pdx::Channel;
-using android::pdx::ErrorStatus;
-using android::pdx::Message;
-using android::pdx::Status;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace {
-
-const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
-const char kDvrDeviceMetricsProperty[] = "ro.dvr.device_metrics";
-const char kDvrDeviceConfigProperty[] = "ro.dvr.device_configuration";
-
-} // namespace
-
-namespace android {
-namespace dvr {
-
-DisplayService::DisplayService(Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback)
- : BASE("DisplayService",
- Endpoint::Create(display::DisplayProtocol::kClientPath)) {
- hardware_composer_.Initialize(
- hidl, primary_display_id, request_display_callback);
-}
-
-bool DisplayService::IsInitialized() const {
- return BASE::IsInitialized() && hardware_composer_.IsInitialized();
-}
-
-std::string DisplayService::DumpState(size_t /*max_length*/) {
- std::ostringstream stream;
-
- auto surfaces = GetDisplaySurfaces();
- std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) {
- return a->surface_id() < b->surface_id();
- });
-
- stream << "Application Surfaces:" << std::endl;
-
- size_t count = 0;
- for (const auto& surface : surfaces) {
- if (surface->surface_type() == SurfaceType::Application) {
- stream << "Surface " << count++ << ":";
- stream << " surface_id=" << surface->surface_id()
- << " process_id=" << surface->process_id()
- << " user_id=" << surface->user_id()
- << " visible=" << surface->visible()
- << " z_order=" << surface->z_order();
-
- stream << " queue_ids=";
- auto queue_ids = surface->GetQueueIds();
- std::sort(queue_ids.begin(), queue_ids.end());
- for (int32_t id : queue_ids) {
- if (id != queue_ids[0])
- stream << ",";
- stream << id;
- }
- stream << std::endl;
- }
- }
- stream << std::endl;
-
- stream << "Direct Surfaces:" << std::endl;
-
- count = 0;
- for (const auto& surface : surfaces) {
- if (surface->surface_type() == SurfaceType::Direct) {
- stream << "Surface " << count++ << ":";
- stream << " surface_id=" << surface->surface_id()
- << " process_id=" << surface->process_id()
- << " user_id=" << surface->user_id()
- << " visible=" << surface->visible()
- << " z_order=" << surface->z_order();
-
- stream << " queue_ids=";
- auto queue_ids = surface->GetQueueIds();
- std::sort(queue_ids.begin(), queue_ids.end());
- for (int32_t id : queue_ids) {
- if (id != queue_ids[0])
- stream << ",";
- stream << id;
- }
- stream << std::endl;
- }
- }
- stream << std::endl;
-
- stream << hardware_composer_.Dump();
- return stream.str();
-}
-
-void DisplayService::OnChannelClose(pdx::Message& message,
- const std::shared_ptr<Channel>& channel) {
- if (auto surface = std::static_pointer_cast<DisplaySurface>(channel)) {
- surface->OnSetAttributes(message,
- {{display::SurfaceAttribute::Visible,
- display::SurfaceAttributeValue{false}}});
- }
-}
-
-// First-level dispatch for display service messages. Directly handles messages
-// that are independent of the display surface (metrics, creation) and routes
-// 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>(
- *this, &DisplayService::OnGetMetrics, message);
- return {};
-
- case DisplayProtocol::GetConfigurationData::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetConfigurationData>(
- *this, &DisplayService::OnGetConfigurationData, message);
- return {};
-
- case DisplayProtocol::GetDisplayIdentificationPort::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(
- *this, &DisplayService::OnGetDisplayIdentificationPort, message);
- return {};
-
- case DisplayProtocol::CreateSurface::Opcode:
- DispatchRemoteMethod<DisplayProtocol::CreateSurface>(
- *this, &DisplayService::OnCreateSurface, message);
- return {};
-
- case DisplayProtocol::SetupGlobalBuffer::Opcode:
- DispatchRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(
- *this, &DisplayService::OnSetupGlobalBuffer, message);
- return {};
-
- case DisplayProtocol::DeleteGlobalBuffer::Opcode:
- DispatchRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(
- *this, &DisplayService::OnDeleteGlobalBuffer, message);
- return {};
-
- case DisplayProtocol::GetGlobalBuffer::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetGlobalBuffer>(
- *this, &DisplayService::OnGetGlobalBuffer, message);
- return {};
-
- case DisplayProtocol::IsVrAppRunning::Opcode:
- DispatchRemoteMethod<DisplayProtocol::IsVrAppRunning>(
- *this, &DisplayService::IsVrAppRunning, message);
- return {};
-
- // Direct the surface specific messages to the surface instance.
- case DisplayProtocol::SetAttributes::Opcode:
- case DisplayProtocol::CreateQueue::Opcode:
- case DisplayProtocol::GetSurfaceInfo::Opcode:
- return HandleSurfaceMessage(message);
-
- default:
- return Service::HandleMessage(message);
- }
-}
-
-Status<display::Metrics> DisplayService::OnGetMetrics(
- pdx::Message& /*message*/) {
- const auto& params = hardware_composer_.GetPrimaryDisplayParams();
- return {{static_cast<uint32_t>(params.width),
- static_cast<uint32_t>(params.height),
- static_cast<uint32_t>(params.dpi.x),
- static_cast<uint32_t>(params.dpi.y),
- static_cast<uint32_t>(params.vsync_period_ns),
- 0,
- 0,
- 0,
- 0.0,
- {},
- {}}};
-}
-
-pdx::Status<std::string> DisplayService::OnGetConfigurationData(
- pdx::Message& /*message*/, display::ConfigFileType config_type) {
- std::string property_name;
- DisplayIdentificationData display_identification_data;
- switch (config_type) {
- case display::ConfigFileType::kLensMetrics:
- property_name = kDvrLensMetricsProperty;
- break;
- case display::ConfigFileType::kDeviceMetrics:
- property_name = kDvrDeviceMetricsProperty;
- break;
- case display::ConfigFileType::kDeviceConfiguration:
- property_name = kDvrDeviceConfigProperty;
- break;
- case display::ConfigFileType::kDeviceEdid:
- display_identification_data =
- hardware_composer_.GetCurrentDisplayIdentificationData();
- if (display_identification_data.size() == 0) {
- return ErrorStatus(ENOENT);
- }
- return std::string(display_identification_data.begin(),
- display_identification_data.end());
- default:
- return ErrorStatus(EINVAL);
- }
- std::string file_path = base::GetProperty(property_name, "");
- if (file_path.empty()) {
- return ErrorStatus(ENOENT);
- }
-
- std::string data;
- if (!base::ReadFileToString(file_path, &data)) {
- return ErrorStatus(errno);
- }
-
- return std::move(data);
-}
-
-pdx::Status<uint8_t> DisplayService::OnGetDisplayIdentificationPort(
- pdx::Message& /*message*/) {
- return hardware_composer_.GetCurrentDisplayPort();
-}
-
-// Creates a new DisplaySurface and associates it with this channel. This may
-// only be done once per channel.
-Status<display::SurfaceInfo> DisplayService::OnCreateSurface(
- pdx::Message& message, const display::SurfaceAttributes& attributes) {
- // A surface may only be created once per channel.
- if (message.GetChannel())
- return ErrorStatus(EINVAL);
-
- ALOGI_IF(TRACE, "DisplayService::OnCreateSurface: cid=%d",
- message.GetChannelId());
-
- // Use the channel id as the unique surface id.
- const int surface_id = message.GetChannelId();
- const int process_id = message.GetProcessId();
- const int user_id = message.GetEffectiveUserId();
-
- ALOGI_IF(TRACE,
- "DisplayService::OnCreateSurface: surface_id=%d process_id=%d",
- surface_id, process_id);
-
- auto surface_status =
- DisplaySurface::Create(this, surface_id, process_id, user_id, attributes);
- if (!surface_status) {
- ALOGE("DisplayService::OnCreateSurface: Failed to create surface: %s",
- surface_status.GetErrorMessage().c_str());
- return ErrorStatus(surface_status.error());
- }
- auto surface = surface_status.take();
- message.SetChannel(surface);
-
- // 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);
-
- return {{surface->surface_id(), surface->visible(), surface->z_order()}};
-}
-
-void DisplayService::SurfaceUpdated(SurfaceType surface_type,
- display::SurfaceUpdateFlags update_flags) {
- ALOGD_IF(TRACE, "DisplayService::SurfaceUpdated: update_flags=%x",
- update_flags.value());
- if (update_flags.value() != 0) {
- if (surface_type == SurfaceType::Application)
- NotifyDisplayConfigurationUpdate();
- else
- UpdateActiveDisplaySurfaces();
- }
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnSetupGlobalBuffer(
- pdx::Message& message, DvrGlobalBufferKey key, size_t size,
- uint64_t usage) {
- const int user_id = message.GetEffectiveUserId();
- const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
-
- if (!trusted) {
- ALOGE(
- "DisplayService::OnSetupGlobalBuffer: Permission denied for user_id=%d",
- user_id);
- return ErrorStatus(EPERM);
- }
- return SetupGlobalBuffer(key, size, usage);
-}
-
-pdx::Status<void> DisplayService::OnDeleteGlobalBuffer(pdx::Message& message,
- DvrGlobalBufferKey key) {
- const int user_id = message.GetEffectiveUserId();
- const bool trusted = (user_id == AID_ROOT) || IsTrustedUid(user_id);
-
- if (!trusted) {
- ALOGE(
- "DisplayService::OnDeleteGlobalBuffer: Permission denied for "
- "user_id=%d",
- user_id);
- return ErrorStatus(EPERM);
- }
- return DeleteGlobalBuffer(key);
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnGetGlobalBuffer(
- pdx::Message& /* message */, DvrGlobalBufferKey key) {
- ALOGD_IF(TRACE, "DisplayService::OnGetGlobalBuffer: key=%d", key);
- auto global_buffer = global_buffers_.find(key);
- if (global_buffer != global_buffers_.end())
- return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
- else
- return pdx::ErrorStatus(EINVAL);
-}
-
-// Calls the message handler for the DisplaySurface associated with this
-// channel.
-Status<void> DisplayService::HandleSurfaceMessage(pdx::Message& message) {
- auto surface = std::static_pointer_cast<DisplaySurface>(message.GetChannel());
- ALOGW_IF(!surface,
- "DisplayService::HandleSurfaceMessage: surface is nullptr!");
-
- if (surface)
- return surface->HandleMessage(message);
- else
- return ErrorStatus(EINVAL);
-}
-
-std::shared_ptr<DisplaySurface> DisplayService::GetDisplaySurface(
- int surface_id) const {
- return std::static_pointer_cast<DisplaySurface>(GetChannel(surface_id));
-}
-
-std::vector<std::shared_ptr<DisplaySurface>>
-DisplayService::GetDisplaySurfaces() const {
- return GetChannels<DisplaySurface>();
-}
-
-std::vector<std::shared_ptr<DirectDisplaySurface>>
-DisplayService::GetVisibleDisplaySurfaces() const {
- std::vector<std::shared_ptr<DirectDisplaySurface>> visible_surfaces;
-
- ForEachDisplaySurface(
- SurfaceType::Direct,
- [&](const std::shared_ptr<DisplaySurface>& surface) mutable {
- if (surface->visible()) {
- visible_surfaces.push_back(
- std::static_pointer_cast<DirectDisplaySurface>(surface));
- surface->ClearUpdate();
- }
- });
-
- return visible_surfaces;
-}
-
-void DisplayService::UpdateActiveDisplaySurfaces() {
- auto visible_surfaces = GetVisibleDisplaySurfaces();
- ALOGD_IF(TRACE,
- "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
- visible_surfaces.size());
- hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::SetupGlobalBuffer(
- DvrGlobalBufferKey key, size_t size, uint64_t usage) {
- auto global_buffer = global_buffers_.find(key);
- if (global_buffer == global_buffers_.end()) {
- auto ion_buffer = std::make_unique<IonBuffer>(static_cast<int>(size), 1,
- HAL_PIXEL_FORMAT_BLOB, usage);
-
- // Some buffers are used internally. If they were configured with an
- // invalid size or format, this will fail.
- int result = hardware_composer_.OnNewGlobalBuffer(key, *ion_buffer.get());
- if (result < 0)
- return ErrorStatus(result);
- global_buffer =
- global_buffers_.insert(std::make_pair(key, std::move(ion_buffer)))
- .first;
- }
-
- return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
-}
-
-pdx::Status<void> DisplayService::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
- auto global_buffer = global_buffers_.find(key);
- if (global_buffer != global_buffers_.end()) {
- // Some buffers are used internally.
- hardware_composer_.OnDeletedGlobalBuffer(key);
- global_buffers_.erase(global_buffer);
- }
-
- return {0};
-}
-
-void DisplayService::SetDisplayConfigurationUpdateNotifier(
- DisplayConfigurationUpdateNotifier update_notifier) {
- update_notifier_ = update_notifier;
-}
-
-void DisplayService::NotifyDisplayConfigurationUpdate() {
- if (update_notifier_)
- update_notifier_();
-}
-
-Status<bool> DisplayService::IsVrAppRunning(pdx::Message& /*message*/) {
- bool visible = false;
- ForEachDisplaySurface(
- SurfaceType::Application,
- [&visible](const std::shared_ptr<DisplaySurface>& surface) {
- if (surface->visible())
- visible = true;
- });
-
- return {visible};
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
deleted file mode 100644
index 89f1eaee33..0000000000
--- a/libs/vr/libvrflinger/display_service.h
+++ /dev/null
@@ -1,126 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
-
-#include <dvr/dvr_api.h>
-#include <pdx/service.h>
-#include <pdx/status.h>
-#include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/display_protocol.h>
-
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "acquired_buffer.h"
-#include "display_surface.h"
-#include "epoll_event_dispatcher.h"
-#include "hardware_composer.h"
-
-namespace android {
-namespace dvr {
-
-// DisplayService implements the display service component of VrFlinger.
-class DisplayService : public pdx::ServiceBase<DisplayService> {
- public:
- bool IsInitialized() const override;
- std::string DumpState(size_t max_length) override;
-
- void OnChannelClose(pdx::Message& message,
- const std::shared_ptr<pdx::Channel>& channel) override;
- pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
- std::shared_ptr<DisplaySurface> GetDisplaySurface(int surface_id) const;
- std::vector<std::shared_ptr<DisplaySurface>> GetDisplaySurfaces() const;
- std::vector<std::shared_ptr<DirectDisplaySurface>> GetVisibleDisplaySurfaces()
- const;
-
- // Updates the list of actively displayed surfaces. This must be called after
- // any change to client/manager attributes that affect visibility or z order.
- void UpdateActiveDisplaySurfaces();
-
- pdx::Status<BorrowedNativeBufferHandle> SetupGlobalBuffer(
- DvrGlobalBufferKey key, size_t size, uint64_t usage);
-
- pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
-
- template <class A>
- void ForEachDisplaySurface(SurfaceType surface_type, A action) const {
- ForEachChannel([surface_type,
- action](const ChannelIterator::value_type& pair) mutable {
- auto surface = std::static_pointer_cast<DisplaySurface>(pair.second);
- if (surface->surface_type() == surface_type)
- action(surface);
- });
- }
-
- using DisplayConfigurationUpdateNotifier = std::function<void(void)>;
- void SetDisplayConfigurationUpdateNotifier(
- DisplayConfigurationUpdateNotifier notifier);
-
- void GrantDisplayOwnership() { hardware_composer_.Enable(); }
- void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
- void OnBootFinished() { hardware_composer_.OnBootFinished(); }
-
- private:
- friend BASE;
- friend DisplaySurface;
-
- friend class VrDisplayStateService;
-
- using RequestDisplayCallback = std::function<void(bool)>;
-
- DisplayService(android::Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback);
-
- pdx::Status<BorrowedNativeBufferHandle> OnGetGlobalBuffer(
- pdx::Message& message, DvrGlobalBufferKey key);
- pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message);
- pdx::Status<std::string> OnGetConfigurationData(
- pdx::Message& message, display::ConfigFileType config_type);
- pdx::Status<uint8_t> OnGetDisplayIdentificationPort(pdx::Message& message);
- pdx::Status<display::SurfaceInfo> OnCreateSurface(
- pdx::Message& message, const display::SurfaceAttributes& attributes);
- pdx::Status<BorrowedNativeBufferHandle> OnSetupGlobalBuffer(
- pdx::Message& message, DvrGlobalBufferKey key, size_t size,
- uint64_t usage);
- pdx::Status<void> OnDeleteGlobalBuffer(pdx::Message& message,
- DvrGlobalBufferKey key);
-
- // Temporary query for current VR status. Will be removed later.
- pdx::Status<bool> IsVrAppRunning(pdx::Message& message);
-
- pdx::Status<void> AddEventHandler(int fd, int events,
- EpollEventDispatcher::Handler handler) {
- return dispatcher_.AddEventHandler(fd, events, handler);
- }
- pdx::Status<void> RemoveEventHandler(int fd) {
- return dispatcher_.RemoveEventHandler(fd);
- }
-
- void SurfaceUpdated(SurfaceType surface_type,
- display::SurfaceUpdateFlags update_flags);
-
- // Called by DisplaySurface to signal that a surface property has changed and
- // the display manager should be notified.
- void NotifyDisplayConfigurationUpdate();
-
- pdx::Status<void> HandleSurfaceMessage(pdx::Message& message);
-
- HardwareComposer hardware_composer_;
- EpollEventDispatcher dispatcher_;
- DisplayConfigurationUpdateNotifier update_notifier_;
-
- std::unordered_map<DvrGlobalBufferKey, std::unique_ptr<IonBuffer>>
- global_buffers_;
-
- DisplayService(const DisplayService&) = delete;
- void operator=(const DisplayService&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
deleted file mode 100644
index 87c823e5b9..0000000000
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ /dev/null
@@ -1,488 +0,0 @@
-#include "display_surface.h"
-
-#include <private/android_filesystem_config.h>
-#include <utils/Trace.h>
-
-#include <private/dvr/trusted_uids.h>
-
-#include "display_service.h"
-#include "hardware_composer.h"
-
-#define LOCAL_TRACE 1
-
-using android::dvr::display::DisplayProtocol;
-using android::pdx::BorrowedChannelHandle;
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Message;
-using android::pdx::RemoteChannelHandle;
-using android::pdx::Status;
-using android::pdx::rpc::DispatchRemoteMethod;
-using android::pdx::rpc::IfAnyOf;
-
-namespace android {
-namespace dvr {
-
-DisplaySurface::DisplaySurface(DisplayService* service,
- SurfaceType surface_type, int surface_id,
- int process_id, int user_id)
- : service_(service),
- surface_type_(surface_type),
- surface_id_(surface_id),
- process_id_(process_id),
- user_id_(user_id),
- update_flags_(display::SurfaceUpdateFlags::NewSurface) {}
-
-DisplaySurface::~DisplaySurface() {
- ALOGD_IF(LOCAL_TRACE,
- "DisplaySurface::~DisplaySurface: surface_id=%d process_id=%d",
- surface_id(), process_id());
-}
-
-Status<void> DisplaySurface::HandleMessage(pdx::Message& message) {
- switch (message.GetOp()) {
- case DisplayProtocol::SetAttributes::Opcode:
- DispatchRemoteMethod<DisplayProtocol::SetAttributes>(
- *this, &DisplaySurface::OnSetAttributes, message);
- break;
-
- case DisplayProtocol::GetSurfaceInfo::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetSurfaceInfo>(
- *this, &DisplaySurface::OnGetSurfaceInfo, message);
- break;
-
- case DisplayProtocol::CreateQueue::Opcode:
- DispatchRemoteMethod<DisplayProtocol::CreateQueue>(
- *this, &DisplaySurface::OnCreateQueue, message);
- break;
- }
-
- return {};
-}
-
-Status<void> DisplaySurface::OnSetAttributes(
- pdx::Message& /*message*/, const display::SurfaceAttributes& attributes) {
- display::SurfaceUpdateFlags update_flags;
-
- for (const auto& attribute : attributes) {
- const auto key = attribute.first;
- const auto* variant = &attribute.second;
- bool invalid_value = false;
- bool visibility_changed = false;
-
- // Catch attributes that have significance to the display service.
- switch (key) {
- case display::SurfaceAttribute::ZOrder:
- invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
- variant, [&](const auto& value) {
- if (z_order_ != value) {
- visibility_changed = true;
- z_order_ = value;
- }
- });
- break;
- case display::SurfaceAttribute::Visible:
- invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
- variant, [&](const auto& value) {
- if (visible_ != value) {
- visibility_changed = true;
- visible_ = value;
- }
- });
- break;
- }
-
- // Only update the attribute map with valid values. This check also has the
- // effect of preventing special attributes handled above from being deleted
- // by an empty value.
- if (invalid_value) {
- ALOGW(
- "DisplaySurface::OnClientSetAttributes: Failed to set display "
- "surface attribute '%d' because of incompatible type: %d",
- key, variant->index());
- } else {
- // An empty value indicates the attribute should be deleted.
- if (variant->empty()) {
- auto search = attributes_.find(key);
- if (search != attributes_.end())
- attributes_.erase(search);
- } else {
- attributes_[key] = *variant;
- }
-
- // All attribute changes generate a notification, even if the value
- // doesn't change. Visibility attributes set a flag only if the value
- // changes.
- update_flags.Set(display::SurfaceUpdateFlags::AttributesChanged);
- if (visibility_changed)
- update_flags.Set(display::SurfaceUpdateFlags::VisibilityChanged);
- }
- }
-
- SurfaceUpdated(update_flags);
- return {};
-}
-
-void DisplaySurface::SurfaceUpdated(display::SurfaceUpdateFlags update_flags) {
- ALOGD_IF(TRACE,
- "DisplaySurface::SurfaceUpdated: surface_id=%d update_flags=0x%x",
- surface_id(), update_flags.value());
-
- update_flags_.Set(update_flags);
- service()->SurfaceUpdated(surface_type(), update_flags_);
-}
-
-void DisplaySurface::ClearUpdate() {
- ALOGD_IF(TRACE > 1, "DisplaySurface::ClearUpdate: surface_id=%d",
- surface_id());
- update_flags_ = display::SurfaceUpdateFlags::None;
-}
-
-Status<display::SurfaceInfo> DisplaySurface::OnGetSurfaceInfo(
- Message& /*message*/) {
- ALOGD_IF(
- TRACE,
- "DisplaySurface::OnGetSurfaceInfo: surface_id=%d visible=%d z_order=%d",
- surface_id(), visible(), z_order());
- return {{surface_id(), visible(), z_order()}};
-}
-
-Status<void> DisplaySurface::RegisterQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue) {
- ALOGD_IF(TRACE, "DisplaySurface::RegisterQueue: surface_id=%d queue_id=%d",
- surface_id(), consumer_queue->id());
- // Capture references for the lambda to work around apparent clang bug.
- // TODO(eieio): Figure out if there is a clang bug or C++11 ambiguity when
- // capturing self and consumer_queue by copy in the following case:
- // auto self = Self();
- // [self, consumer_queue](int events) {
- // self->OnQueueEvent(consuemr_queue, events); }
- //
- struct State {
- std::shared_ptr<DisplaySurface> surface;
- std::shared_ptr<ConsumerQueue> queue;
- };
- State state{Self(), consumer_queue};
-
- return service()->AddEventHandler(
- consumer_queue->queue_fd(), EPOLLIN | EPOLLHUP | EPOLLET,
- [state](int events) {
- state.surface->OnQueueEvent(state.queue, events);
- });
-}
-
-Status<void> DisplaySurface::UnregisterQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue) {
- ALOGD_IF(TRACE, "DisplaySurface::UnregisterQueue: surface_id=%d queue_id=%d",
- surface_id(), consumer_queue->id());
- return service()->RemoveEventHandler(consumer_queue->queue_fd());
-}
-
-void DisplaySurface::OnQueueEvent(
- const std::shared_ptr<ConsumerQueue>& /*consumer_queue*/, int /*events*/) {
- ALOGE(
- "DisplaySurface::OnQueueEvent: ERROR base virtual method should not be "
- "called!!!");
-}
-
-std::shared_ptr<ConsumerQueue> ApplicationDisplaySurface::GetQueue(
- int32_t queue_id) {
- ALOGD_IF(TRACE,
- "ApplicationDisplaySurface::GetQueue: surface_id=%d queue_id=%d",
- surface_id(), queue_id);
-
- std::lock_guard<std::mutex> autolock(lock_);
- auto search = consumer_queues_.find(queue_id);
- if (search != consumer_queues_.end())
- return search->second;
- else
- return nullptr;
-}
-
-std::vector<int32_t> ApplicationDisplaySurface::GetQueueIds() const {
- std::lock_guard<std::mutex> autolock(lock_);
- std::vector<int32_t> queue_ids;
- for (const auto& entry : consumer_queues_)
- queue_ids.push_back(entry.first);
- return queue_ids;
-}
-
-Status<LocalChannelHandle> ApplicationDisplaySurface::OnCreateQueue(
- Message& /*message*/, const ProducerQueueConfig& config) {
- ATRACE_NAME("ApplicationDisplaySurface::OnCreateQueue");
- ALOGD_IF(TRACE,
- "ApplicationDisplaySurface::OnCreateQueue: surface_id=%d, "
- "user_metadata_size=%zu",
- surface_id(), config.user_metadata_size);
-
- std::lock_guard<std::mutex> autolock(lock_);
- auto producer = ProducerQueue::Create(config, UsagePolicy{});
- if (!producer) {
- ALOGE(
- "ApplicationDisplaySurface::OnCreateQueue: Failed to create producer "
- "queue!");
- return ErrorStatus(ENOMEM);
- }
-
- std::shared_ptr<ConsumerQueue> consumer =
- producer->CreateSilentConsumerQueue();
- auto status = RegisterQueue(consumer);
- if (!status) {
- ALOGE(
- "ApplicationDisplaySurface::OnCreateQueue: Failed to register consumer "
- "queue: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- consumer_queues_[consumer->id()] = std::move(consumer);
-
- SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
- return std::move(producer->GetChannelHandle());
-}
-
-void ApplicationDisplaySurface::OnQueueEvent(
- const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
- ALOGD_IF(TRACE,
- "ApplicationDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
- consumer_queue->id(), events);
-
- std::lock_guard<std::mutex> autolock(lock_);
-
- // Always give the queue a chance to handle its internal bookkeeping.
- consumer_queue->HandleQueueEvents();
-
- // Check for hangup and remove a queue that is no longer needed.
- if (consumer_queue->hung_up()) {
- ALOGD_IF(TRACE, "ApplicationDisplaySurface::OnQueueEvent: Removing queue.");
- UnregisterQueue(consumer_queue);
- auto search = consumer_queues_.find(consumer_queue->id());
- if (search != consumer_queues_.end()) {
- consumer_queues_.erase(search);
- } else {
- ALOGE(
- "ApplicationDisplaySurface::OnQueueEvent: Failed to find queue_id=%d",
- consumer_queue->id());
- }
- SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
- }
-}
-
-std::vector<int32_t> DirectDisplaySurface::GetQueueIds() const {
- std::lock_guard<std::mutex> autolock(lock_);
- std::vector<int32_t> queue_ids;
- if (direct_queue_)
- queue_ids.push_back(direct_queue_->id());
- return queue_ids;
-}
-
-Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue(
- Message& /*message*/, const ProducerQueueConfig& config) {
- ATRACE_NAME("DirectDisplaySurface::OnCreateQueue");
- 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_) {
- // Inject the hw composer usage flag to enable the display to read the
- // buffers.
- auto producer = ProducerQueue::Create(
- config, UsagePolicy{GraphicBuffer::USAGE_HW_COMPOSER, 0, 0, 0});
- if (!producer) {
- ALOGE(
- "DirectDisplaySurface::OnCreateQueue: Failed to create producer "
- "queue!");
- return ErrorStatus(ENOMEM);
- }
-
- 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(
- "DirectDisplaySurface::OnCreateQueue: Failed to register consumer "
- "queue: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- return std::move(producer->GetChannelHandle());
- } else {
- return ErrorStatus(EALREADY);
- }
-}
-
-void DirectDisplaySurface::OnQueueEvent(
- const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
- ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
- consumer_queue->id(), events);
-
- std::lock_guard<std::mutex> autolock(lock_);
-
- // Always give the queue a chance to handle its internal bookkeeping.
- consumer_queue->HandleQueueEvents();
-
- // Check for hangup and remove a queue that is no longer needed.
- if (consumer_queue->hung_up()) {
- ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: Removing queue.");
- UnregisterQueue(consumer_queue);
- direct_queue_ = nullptr;
- }
-}
-
-void DirectDisplaySurface::DequeueBuffersLocked() {
- if (direct_queue_ == nullptr) {
- ALOGE(
- "DirectDisplaySurface::DequeueBuffersLocked: Consumer queue is not "
- "initialized.");
- return;
- }
-
- while (true) {
- LocalHandle acquire_fence;
- size_t slot;
- 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,
- "DirectDisplaySurface::DequeueBuffersLocked: All buffers dequeued.");
- ALOGE_IF(buffer_status.error() != ETIMEDOUT,
- "DirectDisplaySurface::DequeueBuffersLocked: Failed to dequeue "
- "buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return;
- }
- auto buffer_consumer = buffer_status.take();
-
- if (!visible()) {
- ATRACE_NAME("DropFrameOnInvisibleSurface");
- ALOGD_IF(TRACE,
- "DirectDisplaySurface::DequeueBuffersLocked: Discarding "
- "buffer_id=%d on invisible surface.",
- buffer_consumer->id());
- buffer_consumer->Discard();
- continue;
- }
-
- if (acquired_buffers_.IsFull()) {
- ALOGE(
- "DirectDisplaySurface::DequeueBuffersLocked: Posted buffers full, "
- "overwriting.");
- acquired_buffers_.PopBack();
- }
-
- acquired_buffers_.Append(
- AcquiredBuffer(buffer_consumer, std::move(acquire_fence), slot));
- }
-}
-
-AcquiredBuffer DirectDisplaySurface::AcquireCurrentBuffer() {
- std::lock_guard<std::mutex> autolock(lock_);
- DequeueBuffersLocked();
-
- if (acquired_buffers_.IsEmpty()) {
- ALOGE(
- "DirectDisplaySurface::AcquireCurrentBuffer: attempt to acquire buffer "
- "when none are posted.");
- return AcquiredBuffer();
- }
- AcquiredBuffer buffer = std::move(acquired_buffers_.Front());
- acquired_buffers_.PopFront();
- ALOGD_IF(TRACE, "DirectDisplaySurface::AcquireCurrentBuffer: buffer_id=%d",
- buffer.buffer()->id());
- return buffer;
-}
-
-AcquiredBuffer DirectDisplaySurface::AcquireNewestAvailableBuffer(
- AcquiredBuffer* skipped_buffer) {
- std::lock_guard<std::mutex> autolock(lock_);
- DequeueBuffersLocked();
-
- AcquiredBuffer buffer;
- int frames = 0;
- // Basic latency stopgap for when the application misses a frame:
- // If the application recovers on the 2nd or 3rd (etc) frame after
- // missing, this code will skip frames to catch up by checking if
- // the next frame is also available.
- while (!acquired_buffers_.IsEmpty() &&
- acquired_buffers_.Front().IsAvailable()) {
- // Capture the skipped buffer into the result parameter.
- // Note that this API only supports skipping one buffer per vsync.
- if (frames > 0 && skipped_buffer)
- *skipped_buffer = std::move(buffer);
- ++frames;
- buffer = std::move(acquired_buffers_.Front());
- acquired_buffers_.PopFront();
- if (frames == 2)
- break;
- }
- ALOGD_IF(TRACE,
- "DirectDisplaySurface::AcquireNewestAvailableBuffer: buffer_id=%d",
- buffer.buffer()->id());
- return buffer;
-}
-
-bool DirectDisplaySurface::IsBufferAvailable() {
- std::lock_guard<std::mutex> autolock(lock_);
- DequeueBuffersLocked();
-
- return !acquired_buffers_.IsEmpty() &&
- acquired_buffers_.Front().IsAvailable();
-}
-
-bool DirectDisplaySurface::IsBufferPosted() {
- std::lock_guard<std::mutex> autolock(lock_);
- DequeueBuffersLocked();
-
- return !acquired_buffers_.IsEmpty();
-}
-
-Status<std::shared_ptr<DisplaySurface>> DisplaySurface::Create(
- DisplayService* service, int surface_id, int process_id, int user_id,
- const display::SurfaceAttributes& attributes) {
- bool direct = false;
- auto search = attributes.find(display::SurfaceAttribute::Direct);
- if (search != attributes.end()) {
- if (!IfAnyOf<int32_t, int64_t, bool, float>::Get(&search->second,
- &direct)) {
- ALOGE(
- "DisplaySurface::Create: Invalid type for SurfaceAttribute::Direct!");
- return ErrorStatus(EINVAL);
- }
- }
-
- ALOGD_IF(TRACE,
- "DisplaySurface::Create: surface_id=%d process_id=%d user_id=%d "
- "direct=%d",
- surface_id, process_id, user_id, direct);
-
- 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)}};
- } else {
- ALOGE(
- "DisplaySurface::Create: Direct surfaces may only be created by "
- "trusted UIDs: user_id=%d",
- user_id);
- return ErrorStatus(EPERM);
- }
- } else {
- return {std::shared_ptr<DisplaySurface>{new ApplicationDisplaySurface(
- service, surface_id, process_id, user_id)}};
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
deleted file mode 100644
index c8b1a078f7..0000000000
--- a/libs/vr/libvrflinger/display_surface.h
+++ /dev/null
@@ -1,188 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
-
-#include <pdx/file_handle.h>
-#include <pdx/service.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/ring_buffer.h>
-
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "acquired_buffer.h"
-
-namespace android {
-namespace dvr {
-
-class DisplayService;
-
-enum class SurfaceType {
- Direct,
- Application,
-};
-
-class DisplaySurface : public pdx::Channel {
- public:
- static pdx::Status<std::shared_ptr<DisplaySurface>> Create(
- DisplayService* service, int surface_id, int process_id, int user_id,
- const display::SurfaceAttributes& attributes);
-
- ~DisplaySurface() override;
-
- DisplayService* service() const { return service_; }
- SurfaceType surface_type() const { return surface_type_; }
- int surface_id() const { return surface_id_; }
- int process_id() const { return process_id_; }
- int user_id() const { return user_id_; }
-
- bool visible() const { return visible_; }
- int z_order() const { return z_order_; }
-
- const display::SurfaceAttributes& attributes() const { return attributes_; }
- display::SurfaceUpdateFlags update_flags() const { return update_flags_; }
-
- virtual std::vector<int32_t> GetQueueIds() const { return {}; }
-
- bool IsUpdatePending() const {
- return update_flags_.value() != display::SurfaceUpdateFlags::None;
- }
-
- protected:
- DisplaySurface(DisplayService* service, SurfaceType surface_type,
- int surface_id, int process_id, int user_id);
-
- // Utility to retrieve a shared pointer to this channel as the desired derived
- // type.
- template <
- typename T = DisplaySurface,
- typename = std::enable_if_t<std::is_base_of<DisplaySurface, T>::value>>
- std::shared_ptr<T> Self() {
- return std::static_pointer_cast<T>(shared_from_this());
- }
-
- virtual pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
- pdx::Message& message, const ProducerQueueConfig& config) = 0;
-
- // Registers a consumer queue with the event dispatcher in DisplayService. The
- // OnQueueEvent callback below is called to handle queue events.
- pdx::Status<void> RegisterQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue);
- pdx::Status<void> UnregisterQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue);
-
- // Called by the event dispatcher in DisplayService when a registered queue
- // event triggers. Executes on the event dispatcher thread.
- virtual void OnQueueEvent(
- const std::shared_ptr<ConsumerQueue>& consumer_queue, int events);
-
- void SurfaceUpdated(display::SurfaceUpdateFlags update_flags);
- void ClearUpdate();
-
- // Synchronizes access to mutable state below between message dispatch thread
- // and frame post thread.
- mutable std::mutex lock_;
-
- private:
- friend class DisplayService;
- friend class DisplayManagerService;
-
- // Dispatches display surface messages to the appropriate handlers. This
- // handler runs on the VrFlinger message dispatch thread.
- pdx::Status<void> HandleMessage(pdx::Message& message);
-
- pdx::Status<void> OnSetAttributes(
- pdx::Message& message, const display::SurfaceAttributes& attributes);
- pdx::Status<display::SurfaceInfo> OnGetSurfaceInfo(pdx::Message& message);
-
- DisplayService* service_;
- SurfaceType surface_type_;
- int surface_id_;
- int process_id_;
- int user_id_;
-
- display::SurfaceAttributes attributes_;
- display::SurfaceUpdateFlags update_flags_ = display::SurfaceUpdateFlags::None;
-
- // Subset of attributes that may be interpreted by the display service.
- bool visible_ = false;
- int z_order_ = 0;
-
- DisplaySurface(const DisplaySurface&) = delete;
- void operator=(const DisplaySurface&) = delete;
-};
-
-class ApplicationDisplaySurface : public DisplaySurface {
- public:
- ApplicationDisplaySurface(DisplayService* service, int surface_id,
- int process_id, int user_id)
- : DisplaySurface(service, SurfaceType::Application, surface_id,
- process_id, user_id) {}
-
- std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id);
- std::vector<int32_t> GetQueueIds() const override;
-
- private:
- pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
- pdx::Message& message, const ProducerQueueConfig& config) override;
- void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
- int events) override;
-
- // Accessed by both message dispatch thread and epoll event thread.
- std::unordered_map<int32_t, std::shared_ptr<ConsumerQueue>> consumer_queues_;
-};
-
-class DirectDisplaySurface : public DisplaySurface {
- public:
- DirectDisplaySurface(DisplayService* service, int surface_id, int process_id,
- int user_id)
- : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id,
- user_id),
- acquired_buffers_(kMaxPostedBuffers),
- metadata_(nullptr) {}
- std::vector<int32_t> GetQueueIds() const override;
- bool IsBufferAvailable();
- bool IsBufferPosted();
- AcquiredBuffer AcquireCurrentBuffer();
-
- // Get the newest buffer. Up to one buffer will be skipped. If a buffer is
- // skipped, it will be stored in skipped_buffer if non null.
- AcquiredBuffer AcquireNewestAvailableBuffer(AcquiredBuffer* skipped_buffer);
-
- private:
- pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
- pdx::Message& message, const ProducerQueueConfig& config) override;
- void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
- int events) override;
-
- // The capacity of the pending buffer queue. Should be enough to hold all the
- // buffers of this DisplaySurface, although in practice only 1 or 2 frames
- // will be pending at a time.
- static constexpr int kSurfaceBufferMaxCount = 4;
- static constexpr int kSurfaceViewMaxCount = 4;
- static constexpr int kMaxPostedBuffers =
- kSurfaceBufferMaxCount * kSurfaceViewMaxCount;
-
- // Returns whether a frame is available without locking the mutex.
- bool IsFrameAvailableNoLock() const;
-
- // Dequeue all available buffers from the consumer queue.
- void DequeueBuffersLocked();
-
- // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be
- // posted and pending.
- 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
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
deleted file mode 100644
index 0d5eb8080f..0000000000
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "epoll_event_dispatcher.h"
-
-#include <log/log.h>
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-#include <sys/prctl.h>
-
-#include <dvr/performance_client_api.h>
-
-namespace android {
-namespace dvr {
-
-EpollEventDispatcher::EpollEventDispatcher() {
- epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
- if (!epoll_fd_) {
- ALOGE("Failed to create epoll fd: %s", strerror(errno));
- return;
- }
-
- event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
- if (!event_fd_) {
- ALOGE("Failed to create event for epolling: %s", strerror(errno));
- return;
- }
-
- // Add watch for eventfd. This should only watch for EPOLLIN, which gets set
- // when eventfd_write occurs. Use "this" as a unique sentinal value to
- // identify events from the event fd.
- epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}};
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
- ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno));
- return;
- }
-
- thread_ = std::thread(&EpollEventDispatcher::EventThread, this);
-}
-
-EpollEventDispatcher::~EpollEventDispatcher() { Stop(); }
-
-void EpollEventDispatcher::Stop() {
- exit_thread_.store(true);
- eventfd_write(event_fd_.Get(), 1);
-}
-
-pdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask,
- Handler handler) {
- std::lock_guard<std::mutex> lock(lock_);
-
- epoll_event event;
- event.events = event_mask;
- event.data.ptr = &(handlers_[fd] = handler);
-
- ALOGD_IF(
- TRACE,
- "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p",
- fd, event_mask, event.data.ptr);
-
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) {
- const int error = errno;
- ALOGE("Failed to add fd to epoll set because: %s", strerror(error));
- return pdx::ErrorStatus(error);
- } else {
- return {};
- }
-}
-
-pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) {
- ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
- std::lock_guard<std::mutex> lock(lock_);
-
- epoll_event ee; // See BUGS in man 2 epoll_ctl.
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) {
- const int error = errno;
- ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
- return pdx::ErrorStatus(error);
- }
-
- // If the fd was valid above, add it to the list of ids to remove.
- removed_handlers_.push_back(fd);
-
- // Wake up the event thread to clean up.
- eventfd_write(event_fd_.Get(), 1);
-
- return {};
-}
-
-void EpollEventDispatcher::EventThread() {
- prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0);
-
- const int error = dvrSetSchedulerClass(0, "graphics");
- LOG_ALWAYS_FATAL_IF(
- error < 0,
- "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s",
- strerror(-error));
-
- const size_t kMaxNumEvents = 128;
- epoll_event events[kMaxNumEvents];
-
- while (!exit_thread_.load()) {
- const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1);
- if (num_events < 0 && errno != EINTR)
- break;
-
- ALOGD_IF(TRACE > 1, "EpollEventDispatcher::EventThread: num_events=%d",
- num_events);
-
- for (int i = 0; i < num_events; i++) {
- ALOGD_IF(
- TRACE > 1,
- "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x",
- i, events[i].data.ptr, events[i].events);
-
- if (events[i].data.ptr == this) {
- // Clear pending event on event_fd_. Serialize the read with respect to
- // writes from other threads.
- std::lock_guard<std::mutex> lock(lock_);
- eventfd_t value;
- eventfd_read(event_fd_.Get(), &value);
- } else {
- auto handler = reinterpret_cast<Handler*>(events[i].data.ptr);
- if (handler)
- (*handler)(events[i].events);
- }
- }
-
- // Remove any handlers that have been posted for removal. This is done here
- // instead of in RemoveEventHandler() to prevent races between the dispatch
- // thread and the code requesting the removal. Handlers are guaranteed to
- // stay alive between exiting epoll_wait() and the dispatch loop above.
- std::lock_guard<std::mutex> lock(lock_);
- for (auto handler_fd : removed_handlers_) {
- ALOGD_IF(TRACE,
- "EpollEventDispatcher::EventThread: removing handler: fd=%d",
- handler_fd);
- handlers_.erase(handler_fd);
- }
- removed_handlers_.clear();
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.h b/libs/vr/libvrflinger/epoll_event_dispatcher.h
deleted file mode 100644
index eb687f4e86..0000000000
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
-
-#include <sys/epoll.h>
-
-#include <atomic>
-#include <functional>
-#include <mutex>
-#include <thread>
-#include <unordered_map>
-#include <vector>
-
-#include <pdx/file_handle.h>
-#include <pdx/status.h>
-
-namespace android {
-namespace dvr {
-
-class EpollEventDispatcher {
- public:
- // Function type for event handlers. The handler receives a bitmask of the
- // epoll events that occurred on the file descriptor associated with the
- // handler.
- using Handler = std::function<void(int)>;
-
- EpollEventDispatcher();
- ~EpollEventDispatcher();
-
- // |handler| is called on the internal dispatch thread when |fd| is signaled
- // by events in |event_mask|.
- pdx::Status<void> AddEventHandler(int fd, int event_mask, Handler handler);
- pdx::Status<void> RemoveEventHandler(int fd);
-
- void Stop();
-
- private:
- void EventThread();
-
- std::thread thread_;
- std::atomic<bool> exit_thread_{false};
-
- // Protects handlers_ and removed_handlers_ and serializes operations on
- // epoll_fd_ and event_fd_.
- std::mutex lock_;
-
- // Maintains a map of fds to event handlers. This is primarily to keep any
- // references alive that may be bound in the std::function instances. It is
- // not used at dispatch time to avoid performance problems with different
- // versions of std::unordered_map.
- std::unordered_map<int, Handler> handlers_;
-
- // List of fds to be removed from the map. The actual removal is performed
- // by the event dispatch thread to avoid races.
- std::vector<int> removed_handlers_;
-
- pdx::LocalHandle epoll_fd_;
- pdx::LocalHandle event_fd_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
deleted file mode 100644
index 70f303b208..0000000000
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ /dev/null
@@ -1,1541 +0,0 @@
-#include "hardware_composer.h"
-
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <cutils/sched_policy.h>
-#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>
-
-#include <algorithm>
-#include <chrono>
-#include <functional>
-#include <map>
-#include <sstream>
-#include <string>
-#include <tuple>
-
-#include <dvr/dvr_display_types.h>
-#include <dvr/performance_client_api.h>
-#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;
-
-using namespace std::chrono_literals;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-const char kDvrPerformanceProperty[] = "sys.dvr.performance";
-
-const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
-
-// Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these
-// events. Name ours similarly.
-const char kVsyncTraceEventName[] = "VSYNC-vrflinger";
-
-// How long to wait after boot finishes before we turn the display off.
-constexpr int kBootFinishedDisplayOffTimeoutSec = 10;
-
-constexpr int kDefaultDisplayWidth = 1920;
-constexpr int kDefaultDisplayHeight = 1080;
-constexpr int64_t kDefaultVsyncPeriodNs = 16666667;
-// Hardware composer reports dpi as dots per thousand inches (dpi * 1000).
-constexpr int kDefaultDpi = 400000;
-
-// Get time offset from a vsync to when the pose for that vsync should be
-// predicted out to. For example, if scanout gets halfway through the frame
-// at the halfway point between vsyncs, then this could be half the period.
-// With global shutter displays, this should be changed to the offset to when
-// illumination begins. Low persistence adds a frame of latency, so we predict
-// to the center of the next frame.
-inline int64_t GetPosePredictionTimeOffset(int64_t vsync_period_ns) {
- return (vsync_period_ns * 150) / 100;
-}
-
-// Attempts to set the scheduler class and partiton for the current thread.
-// Returns true on success or false on failure.
-bool SetThreadPolicy(const std::string& scheduler_class,
- const std::string& partition) {
- int error = dvrSetSchedulerClass(0, scheduler_class.c_str());
- if (error < 0) {
- ALOGE(
- "SetThreadPolicy: Failed to set scheduler class \"%s\" for "
- "thread_id=%d: %s",
- scheduler_class.c_str(), gettid(), strerror(-error));
- return false;
- }
- error = dvrSetCpuPartition(0, partition.c_str());
- if (error < 0) {
- ALOGE(
- "SetThreadPolicy: Failed to set cpu partiton \"%s\" for thread_id=%d: "
- "%s",
- partition.c_str(), gettid(), strerror(-error));
- return false;
- }
- return true;
-}
-
-// Utility to generate scoped tracers with arguments.
-// TODO(eieio): Move/merge this into utils/Trace.h?
-class TraceArgs {
- public:
- template <typename... Args>
- explicit 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());
- }
-
- ~TraceArgs() { atrace_end(ATRACE_TAG); }
-
- private:
- TraceArgs(const TraceArgs&) = delete;
- void operator=(const TraceArgs&) = delete;
-};
-
-// 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__ }
-
-// Returns "primary" or "external". Useful for writing more readable logs.
-const char* GetDisplayName(bool is_primary) {
- return is_primary ? "primary" : "external";
-}
-
-} // anonymous namespace
-
-HardwareComposer::HardwareComposer()
- : initialized_(false), request_display_callback_(nullptr) {}
-
-HardwareComposer::~HardwareComposer(void) {
- UpdatePostThreadState(PostThreadState::Quit, true);
- if (post_thread_.joinable())
- post_thread_.join();
- composer_callback_->SetVsyncService(nullptr);
-}
-
-void HardwareComposer::UpdateEdidData(Hwc2::Composer* composer,
- hwc2_display_t hw_id) {
- const auto error = composer->getDisplayIdentificationData(
- hw_id, &display_port_, &display_identification_data_);
- if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
- if (error !=
- android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
- ALOGI("hardware_composer: identification data error\n");
- } else {
- ALOGI("hardware_composer: identification data unsupported\n");
- }
- }
-}
-
-bool HardwareComposer::Initialize(
- Hwc2::Composer* composer, hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback) {
- if (initialized_) {
- ALOGE("HardwareComposer::Initialize: already initialized.");
- return false;
- }
-
- request_display_callback_ = request_display_callback;
-
- primary_display_ = GetDisplayParams(composer, primary_display_id, true);
-
- vsync_service_ = new VsyncService;
- sp<IServiceManager> sm(defaultServiceManager());
- auto result = sm->addService(String16(VsyncService::GetServiceName()),
- vsync_service_, false);
- LOG_ALWAYS_FATAL_IF(result != android::OK,
- "addService(%s) failed", VsyncService::GetServiceName());
-
- post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
- LOG_ALWAYS_FATAL_IF(
- !post_thread_event_fd_,
- "HardwareComposer: Failed to create interrupt event fd : %s",
- strerror(errno));
-
- UpdateEdidData(composer, primary_display_id);
-
- post_thread_ = std::thread(&HardwareComposer::PostThread, this);
-
- initialized_ = true;
-
- return initialized_;
-}
-
-void HardwareComposer::Enable() {
- UpdatePostThreadState(PostThreadState::Suspended, false);
-}
-
-void HardwareComposer::Disable() {
- UpdatePostThreadState(PostThreadState::Suspended, true);
-
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- post_thread_ready_.wait(lock, [this] {
- return !post_thread_resumed_;
- });
-}
-
-void HardwareComposer::OnBootFinished() {
- std::lock_guard<std::mutex> lock(post_thread_mutex_);
- if (boot_finished_)
- return;
- boot_finished_ = true;
- post_thread_wait_.notify_one();
-}
-
-// Update the post thread quiescent state based on idle and suspended inputs.
-void HardwareComposer::UpdatePostThreadState(PostThreadStateType state,
- bool suspend) {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
-
- // Update the votes in the state variable before evaluating the effective
- // quiescent state. Any bits set in post_thread_state_ indicate that the post
- // thread should be suspended.
- if (suspend) {
- post_thread_state_ |= state;
- } else {
- post_thread_state_ &= ~state;
- }
-
- const bool quit = post_thread_state_ & PostThreadState::Quit;
- const bool effective_suspend = post_thread_state_ != PostThreadState::Active;
- if (quit) {
- post_thread_quiescent_ = true;
- eventfd_write(post_thread_event_fd_.Get(), 1);
- post_thread_wait_.notify_one();
- } else if (effective_suspend && !post_thread_quiescent_) {
- post_thread_quiescent_ = true;
- eventfd_write(post_thread_event_fd_.Get(), 1);
- } else if (!effective_suspend && post_thread_quiescent_) {
- post_thread_quiescent_ = false;
- eventfd_t value;
- eventfd_read(post_thread_event_fd_.Get(), &value);
- post_thread_wait_.notify_one();
- }
-}
-
-void HardwareComposer::CreateComposer() {
- if (composer_)
- return;
- composer_.reset(new Hwc2::impl::Composer("default"));
- composer_callback_ = new ComposerCallback;
- composer_->registerCallback(composer_callback_);
- LOG_ALWAYS_FATAL_IF(!composer_callback_->GotFirstHotplug(),
- "Registered composer callback but didn't get hotplug for primary"
- " display");
- composer_callback_->SetVsyncService(vsync_service_);
-}
-
-void HardwareComposer::OnPostThreadResumed() {
- ALOGI("OnPostThreadResumed");
- EnableDisplay(*target_display_, true);
-
- // Trigger target-specific performance mode change.
- property_set(kDvrPerformanceProperty, "performance");
-}
-
-void HardwareComposer::OnPostThreadPaused() {
- ALOGI("OnPostThreadPaused");
- retire_fence_fds_.clear();
- layers_.clear();
-
- // Phones create a new composer client on resume and destroy it on pause.
- if (composer_callback_ != nullptr) {
- composer_callback_->SetVsyncService(nullptr);
- composer_callback_ = nullptr;
- }
- composer_.reset(nullptr);
-
- // Trigger target-specific performance mode change.
- property_set(kDvrPerformanceProperty, "idle");
-}
-
-bool HardwareComposer::PostThreadCondWait(std::unique_lock<std::mutex>& lock,
- int timeout_sec,
- const std::function<bool()>& pred) {
- auto pred_with_quit = [&] {
- return pred() || (post_thread_state_ & PostThreadState::Quit);
- };
- if (timeout_sec >= 0) {
- post_thread_wait_.wait_for(lock, std::chrono::seconds(timeout_sec),
- pred_with_quit);
- } else {
- post_thread_wait_.wait(lock, pred_with_quit);
- }
- if (post_thread_state_ & PostThreadState::Quit) {
- ALOGI("HardwareComposer::PostThread: Quitting.");
- return true;
- }
- return false;
-}
-
-HWC::Error HardwareComposer::Validate(hwc2_display_t display) {
- uint32_t num_types;
- uint32_t num_requests;
- HWC::Error error =
- composer_->validateDisplay(display, &num_types, &num_requests);
-
- if (error == HWC2_ERROR_HAS_CHANGES) {
- ALOGE("Hardware composer has requested composition changes, "
- "which we don't support.");
- // Accept the changes anyway and see if we can get something on the screen.
- error = composer_->acceptDisplayChanges(display);
- }
-
- return error;
-}
-
-bool HardwareComposer::EnableVsync(const DisplayParams& display, bool enabled) {
- HWC::Error error = composer_->setVsyncEnabled(display.id,
- (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
- : HWC2_VSYNC_DISABLE));
- if (error != HWC::Error::None) {
- ALOGE("Error attempting to %s vsync on %s display: %s",
- enabled ? "enable" : "disable", GetDisplayName(display.is_primary),
- error.to_string().c_str());
- }
- return error == HWC::Error::None;
-}
-
-bool HardwareComposer::SetPowerMode(const DisplayParams& display, bool active) {
- ALOGI("Turning %s display %s", GetDisplayName(display.is_primary),
- active ? "on" : "off");
- HWC::PowerMode power_mode = active ? HWC::PowerMode::On : HWC::PowerMode::Off;
- HWC::Error error = composer_->setPowerMode(display.id,
- power_mode.cast<Hwc2::IComposerClient::PowerMode>());
- if (error != HWC::Error::None) {
- ALOGE("Error attempting to turn %s display %s: %s",
- GetDisplayName(display.is_primary), active ? "on" : "off",
- error.to_string().c_str());
- }
- return error == HWC::Error::None;
-}
-
-bool HardwareComposer::EnableDisplay(const DisplayParams& display,
- bool enabled) {
- bool power_result;
- bool vsync_result;
- // When turning a display on, we set the power state then set vsync. When
- // turning a display off we do it in the opposite order.
- if (enabled) {
- power_result = SetPowerMode(display, enabled);
- vsync_result = EnableVsync(display, enabled);
- } else {
- vsync_result = EnableVsync(display, enabled);
- power_result = SetPowerMode(display, enabled);
- }
- return power_result && vsync_result;
-}
-
-HWC::Error HardwareComposer::Present(hwc2_display_t display) {
- int32_t 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.
- if (error == HWC::Error::None) {
- retire_fence_fds_.emplace_back(present_fence);
- } else {
- ATRACE_INT("HardwareComposer: PresentResult", error);
- }
-
- return error;
-}
-
-DisplayParams HardwareComposer::GetDisplayParams(
- Hwc2::Composer* composer, hwc2_display_t display, bool is_primary) {
- DisplayParams params;
- params.id = display;
- params.is_primary = is_primary;
-
- Hwc2::Config config;
- HWC::Error error = composer->getActiveConfig(display, &config);
-
- if (error == HWC::Error::None) {
- auto get_attr = [&](hwc2_attribute_t attr, const char* attr_name)
- -> std::optional<int32_t> {
- int32_t val;
- HWC::Error error = composer->getDisplayAttribute(
- display, config, (Hwc2::IComposerClient::Attribute)attr, &val);
- if (error != HWC::Error::None) {
- ALOGE("Failed to get %s display attr %s: %s",
- GetDisplayName(is_primary), attr_name,
- error.to_string().c_str());
- return std::nullopt;
- }
- return val;
- };
-
- auto width = get_attr(HWC2_ATTRIBUTE_WIDTH, "width");
- auto height = get_attr(HWC2_ATTRIBUTE_HEIGHT, "height");
-
- if (width && height) {
- params.width = *width;
- params.height = *height;
- } else {
- ALOGI("Failed to get width and/or height for %s display. Using default"
- " size %dx%d.", GetDisplayName(is_primary), kDefaultDisplayWidth,
- kDefaultDisplayHeight);
- params.width = kDefaultDisplayWidth;
- params.height = kDefaultDisplayHeight;
- }
-
- auto vsync_period = get_attr(HWC2_ATTRIBUTE_VSYNC_PERIOD, "vsync period");
- if (vsync_period) {
- params.vsync_period_ns = *vsync_period;
- } else {
- ALOGI("Failed to get vsync period for %s display. Using default vsync"
- " period %.2fms", GetDisplayName(is_primary),
- static_cast<float>(kDefaultVsyncPeriodNs) / 1000000);
- params.vsync_period_ns = kDefaultVsyncPeriodNs;
- }
-
- auto dpi_x = get_attr(HWC2_ATTRIBUTE_DPI_X, "DPI X");
- auto dpi_y = get_attr(HWC2_ATTRIBUTE_DPI_Y, "DPI Y");
- if (dpi_x && dpi_y) {
- params.dpi.x = *dpi_x;
- params.dpi.y = *dpi_y;
- } else {
- ALOGI("Failed to get dpi_x and/or dpi_y for %s display. Using default"
- " dpi %d.", GetDisplayName(is_primary), kDefaultDpi);
- params.dpi.x = kDefaultDpi;
- params.dpi.y = kDefaultDpi;
- }
- } else {
- ALOGE("HardwareComposer: Failed to get current %s display config: %d."
- " Using default display values.",
- GetDisplayName(is_primary), error.value);
- params.width = kDefaultDisplayWidth;
- params.height = kDefaultDisplayHeight;
- params.dpi.x = kDefaultDpi;
- params.dpi.y = kDefaultDpi;
- params.vsync_period_ns = kDefaultVsyncPeriodNs;
- }
-
- ALOGI(
- "HardwareComposer: %s display attributes: width=%d height=%d "
- "vsync_period_ns=%d DPI=%dx%d",
- GetDisplayName(is_primary),
- params.width,
- params.height,
- params.vsync_period_ns,
- params.dpi.x,
- params.dpi.y);
-
- return params;
-}
-
-std::string HardwareComposer::Dump() {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- std::ostringstream stream;
-
- auto print_display_metrics = [&](const DisplayParams& params) {
- stream << GetDisplayName(params.is_primary)
- << " display metrics: " << params.width << "x"
- << params.height << " " << (params.dpi.x / 1000.0)
- << "x" << (params.dpi.y / 1000.0) << " dpi @ "
- << (1000000000.0 / params.vsync_period_ns) << " Hz"
- << std::endl;
- };
-
- print_display_metrics(primary_display_);
- if (external_display_)
- print_display_metrics(*external_display_);
-
- stream << "Post thread resumed: " << post_thread_resumed_ << std::endl;
- stream << "Active layers: " << layers_.size() << std::endl;
- stream << std::endl;
-
- 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();
- stream << " buffer_id=" << layers_[i].GetBufferId();
- stream << std::endl;
- }
- stream << std::endl;
-
- if (post_thread_resumed_) {
- stream << "Hardware Composer Debug Info:" << std::endl;
- stream << composer_->dumpDebugInfo();
- }
-
- return stream.str();
-}
-
-void HardwareComposer::PostLayers(hwc2_display_t display) {
- ATRACE_NAME("HardwareComposer::PostLayers");
-
- // Setup the hardware composer layers with current buffers.
- for (auto& layer : layers_) {
- layer.Prepare();
- }
-
- // Now that we have taken in a frame from the application, we have a chance
- // to drop the frame before passing the frame along to HWC.
- // If the display driver has become backed up, we detect it here and then
- // react by skipping this frame to catch up latency.
- while (!retire_fence_fds_.empty() &&
- (!retire_fence_fds_.front() ||
- sync_wait(retire_fence_fds_.front().Get(), 0) == 0)) {
- // There are only 2 fences in here, no performance problem to shift the
- // array of ints.
- retire_fence_fds_.erase(retire_fence_fds_.begin());
- }
-
- const bool is_fence_pending = static_cast<int32_t>(retire_fence_fds_.size()) >
- post_thread_config_.allowed_pending_fence_count;
-
- if (is_fence_pending) {
- ATRACE_INT("frame_skip_count", ++frame_skip_count_);
-
- ALOGW_IF(is_fence_pending,
- "Warning: dropping a frame to catch up with HWC (pending = %zd)",
- retire_fence_fds_.size());
-
- for (auto& layer : layers_) {
- layer.Drop();
- }
- return;
- } else {
- // Make the transition more obvious in systrace when the frame skip happens
- // above.
- ATRACE_INT("frame_skip_count", 0);
- }
-
-#if TRACE > 1
- 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());
- }
-#endif
-
- HWC::Error error = Validate(display);
- if (error != HWC::Error::None) {
- ALOGE("HardwareComposer::PostLayers: Validate failed: %s display=%" PRIu64,
- error.to_string().c_str(), display);
- return;
- }
-
- error = Present(display);
- if (error != HWC::Error::None) {
- ALOGE("HardwareComposer::PostLayers: Present failed: %s",
- error.to_string().c_str());
- return;
- }
-
- std::vector<Hwc2::Layer> out_layers;
- std::vector<int> out_fences;
- error = composer_->getReleaseFences(display,
- &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.
- uint32_t num_elements = out_layers.size();
- for (size_t i = 0; i < num_elements; ++i) {
- for (auto& layer : layers_) {
- if (layer.GetLayerHandle() == out_layers[i]) {
- layer.Finish(out_fences[i]);
- }
- }
- }
-}
-
-void HardwareComposer::SetDisplaySurfaces(
- std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces) {
- ALOGI("HardwareComposer::SetDisplaySurfaces: surface count=%zd",
- surfaces.size());
- const bool display_idle = surfaces.size() == 0;
- {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- surfaces_ = std::move(surfaces);
- surfaces_changed_ = true;
- }
-
- if (request_display_callback_)
- request_display_callback_(!display_idle);
-
- // Set idle state based on whether there are any surfaces to handle.
- UpdatePostThreadState(PostThreadState::Idle, display_idle);
-}
-
-int HardwareComposer::OnNewGlobalBuffer(DvrGlobalBufferKey key,
- IonBuffer& ion_buffer) {
- if (key == DvrGlobalBuffers::kVsyncBuffer) {
- vsync_ring_ = std::make_unique<CPUMappedBroadcastRing<DvrVsyncRing>>(
- &ion_buffer, CPUUsageMode::WRITE_OFTEN);
-
- if (vsync_ring_->IsMapped() == false) {
- return -EPERM;
- }
- }
-
- if (key == DvrGlobalBuffers::kVrFlingerConfigBufferKey) {
- return MapConfigBuffer(ion_buffer);
- }
-
- return 0;
-}
-
-void HardwareComposer::OnDeletedGlobalBuffer(DvrGlobalBufferKey key) {
- if (key == DvrGlobalBuffers::kVrFlingerConfigBufferKey) {
- ConfigBufferDeleted();
- }
-}
-
-int HardwareComposer::MapConfigBuffer(IonBuffer& ion_buffer) {
- std::lock_guard<std::mutex> lock(shared_config_mutex_);
- shared_config_ring_ = DvrConfigRing();
-
- if (ion_buffer.width() < DvrConfigRing::MemorySize()) {
- ALOGE("HardwareComposer::MapConfigBuffer: invalid buffer size.");
- return -EINVAL;
- }
-
- void* buffer_base = 0;
- int result = ion_buffer.Lock(ion_buffer.usage(), 0, 0, ion_buffer.width(),
- ion_buffer.height(), &buffer_base);
- if (result != 0) {
- ALOGE(
- "HardwareComposer::MapConfigBuffer: Failed to map vrflinger config "
- "buffer.");
- return -EPERM;
- }
-
- shared_config_ring_ = DvrConfigRing::Create(buffer_base, ion_buffer.width());
- ion_buffer.Unlock();
-
- return 0;
-}
-
-void HardwareComposer::ConfigBufferDeleted() {
- std::lock_guard<std::mutex> lock(shared_config_mutex_);
- shared_config_ring_ = DvrConfigRing();
-}
-
-void HardwareComposer::UpdateConfigBuffer() {
- std::lock_guard<std::mutex> lock(shared_config_mutex_);
- if (!shared_config_ring_.is_valid())
- return;
- // Copy from latest record in shared_config_ring_ to local copy.
- DvrConfig record;
- if (shared_config_ring_.GetNewest(&shared_config_ring_sequence_, &record)) {
- ALOGI("DvrConfig updated: sequence %u, post offset %d",
- shared_config_ring_sequence_, record.frame_post_offset_ns);
- ++shared_config_ring_sequence_;
- post_thread_config_ = record;
- }
-}
-
-int HardwareComposer::PostThreadPollInterruptible(
- const pdx::LocalHandle& event_fd, int requested_events, int timeout_ms) {
- pollfd pfd[2] = {
- {
- .fd = event_fd.Get(),
- .events = static_cast<short>(requested_events),
- .revents = 0,
- },
- {
- .fd = post_thread_event_fd_.Get(),
- .events = POLLPRI | POLLIN,
- .revents = 0,
- },
- };
- int ret, error;
- do {
- ret = poll(pfd, 2, timeout_ms);
- error = errno;
- ALOGW_IF(ret < 0,
- "HardwareComposer::PostThreadPollInterruptible: Error during "
- "poll(): %s (%d)",
- strerror(error), error);
- } while (ret < 0 && error == EINTR);
-
- 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: revents=%x", pfd[1].revents);
- return kPostThreadInterrupted;
- } else {
- return 0;
- }
-}
-
-// Sleep until the next predicted vsync, returning the predicted vsync
-// timestamp.
-Status<int64_t> HardwareComposer::WaitForPredictedVSync() {
- const int64_t predicted_vsync_time = last_vsync_timestamp_ +
- (target_display_->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) {
- const int timer_fd = vsync_sleep_timer_fd_.Get();
- const itimerspec wakeup_itimerspec = {
- .it_interval = {.tv_sec = 0, .tv_nsec = 0},
- .it_value = NsToTimespec(wakeup_timestamp),
- };
- int ret =
- timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &wakeup_itimerspec, nullptr);
- int error = errno;
- if (ret < 0) {
- ALOGE("HardwareComposer::SleepUntil: Failed to set timerfd: %s",
- strerror(error));
- return -error;
- }
-
- return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN,
- /*timeout_ms*/ -1);
-}
-
-void HardwareComposer::PostThread() {
- // NOLINTNEXTLINE(runtime/int)
- prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrHwcPost"), 0, 0, 0);
-
- // Set the scheduler to SCHED_FIFO with high priority. If this fails here
- // there may have been a startup timing issue between this thread and
- // performanced. Try again later when this thread becomes active.
- bool thread_policy_setup =
- SetThreadPolicy("graphics:high", "/system/performance");
-
- // Create a timerfd based on CLOCK_MONOTINIC.
- vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
- LOG_ALWAYS_FATAL_IF(
- !vsync_sleep_timer_fd_,
- "HardwareComposer: Failed to create vsync sleep timerfd: %s",
- strerror(errno));
-
- struct VsyncEyeOffsets { int64_t left_ns, right_ns; };
- bool was_running = false;
-
- auto get_vsync_eye_offsets = [this]() -> VsyncEyeOffsets {
- VsyncEyeOffsets offsets;
- offsets.left_ns =
- GetPosePredictionTimeOffset(target_display_->vsync_period_ns);
-
- // TODO(jbates) Query vblank time from device, when such an API is
- // available. This value (6.3%) was measured on A00 in low persistence mode.
- int64_t vblank_ns = target_display_->vsync_period_ns * 63 / 1000;
- offsets.right_ns = (target_display_->vsync_period_ns - vblank_ns) / 2;
-
- // Check property for overriding right eye offset value.
- offsets.right_ns =
- property_get_int64(kRightEyeOffsetProperty, offsets.right_ns);
-
- return offsets;
- };
-
- VsyncEyeOffsets vsync_eye_offsets = get_vsync_eye_offsets();
-
- while (1) {
- ATRACE_NAME("HardwareComposer::PostThread");
-
- // Check for updated config once per vsync.
- UpdateConfigBuffer();
-
- while (post_thread_quiescent_) {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
-
- if (was_running) {
- vsync_trace_parity_ = false;
- ATRACE_INT(kVsyncTraceEventName, 0);
- }
-
- // Tear down resources.
- OnPostThreadPaused();
- was_running = false;
- post_thread_resumed_ = false;
- post_thread_ready_.notify_all();
-
- if (PostThreadCondWait(lock, -1,
- [this] { return !post_thread_quiescent_; })) {
- // A true return value means we've been asked to quit.
- return;
- }
-
- post_thread_resumed_ = true;
- post_thread_ready_.notify_all();
-
- ALOGI("HardwareComposer::PostThread: Exiting quiescent state.");
- }
-
- if (!composer_)
- CreateComposer();
-
- bool target_display_changed = UpdateTargetDisplay();
- bool just_resumed_running = !was_running;
- was_running = true;
-
- if (target_display_changed)
- vsync_eye_offsets = get_vsync_eye_offsets();
-
- if (just_resumed_running) {
- OnPostThreadResumed();
-
- // Try to setup the scheduler policy if it failed during startup. Only
- // attempt to do this on transitions from inactive to active to avoid
- // spamming the system with RPCs and log messages.
- if (!thread_policy_setup) {
- thread_policy_setup =
- SetThreadPolicy("graphics:high", "/system/performance");
- }
- }
-
- if (target_display_changed || just_resumed_running) {
- // 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();
- vsync_prediction_interval_ = 1;
- retire_fence_fds_.clear();
- }
-
- int64_t vsync_timestamp = 0;
- {
- TRACE_FORMAT("wait_vsync|vsync=%u;last_timestamp=%" PRId64
- ";prediction_interval=%d|",
- vsync_count_ + 1, last_vsync_timestamp_,
- vsync_prediction_interval_);
-
- auto status = WaitForPredictedVSync();
- ALOGE_IF(
- !status,
- "HardwareComposer::PostThread: Failed to wait for vsync event: %s",
- 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_trace_parity_ = !vsync_trace_parity_;
- ATRACE_INT(kVsyncTraceEventName, vsync_trace_parity_ ? 1 : 0);
-
- // 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_;
-
- UpdateLayerConfig();
-
- // Publish the vsync event.
- if (vsync_ring_) {
- DvrVsync vsync;
- vsync.vsync_count = vsync_count_;
- vsync.vsync_timestamp_ns = vsync_timestamp;
- vsync.vsync_left_eye_offset_ns = vsync_eye_offsets.left_ns;
- vsync.vsync_right_eye_offset_ns = vsync_eye_offsets.right_ns;
- vsync.vsync_period_ns = target_display_->vsync_period_ns;
-
- vsync_ring_->Publish(vsync);
- }
-
- {
- // Sleep until shortly before vsync.
- ATRACE_NAME("sleep");
-
- const int64_t display_time_est_ns =
- vsync_timestamp + target_display_->vsync_period_ns;
- const int64_t now_ns = GetSystemClockNs();
- const int64_t sleep_time_ns = display_time_est_ns - now_ns -
- post_thread_config_.frame_post_offset_ns;
- const int64_t wakeup_time_ns =
- display_time_est_ns - post_thread_config_.frame_post_offset_ns;
-
- ATRACE_INT64("sleep_time_ns", sleep_time_ns);
- if (sleep_time_ns > 0) {
- int error = SleepUntil(wakeup_time_ns);
- ALOGE_IF(error < 0 && error != kPostThreadInterrupted,
- "HardwareComposer::PostThread: Failed to sleep: %s",
- strerror(-error));
- // If the sleep was interrupted (error == kPostThreadInterrupted),
- // we still go through and present this frame because we may have set
- // layers earlier and we want to flush the Composer's internal command
- // buffer by continuing through to validate and present.
- }
- }
-
- {
- auto status = composer_callback_->GetVsyncTime(target_display_->id);
-
- // 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(target_display_->id);
- }
-}
-
-bool HardwareComposer::UpdateTargetDisplay() {
- bool target_display_changed = false;
- auto displays = composer_callback_->GetDisplays();
- if (displays.external_display_was_hotplugged) {
- bool was_using_external_display = !target_display_->is_primary;
- if (was_using_external_display) {
- // The external display was hotplugged, so make sure to ignore any bad
- // display errors as we destroy the layers.
- for (auto& layer: layers_)
- layer.IgnoreBadDisplayErrorsOnDestroy(true);
- }
-
- if (displays.external_display) {
- // External display was connected
- external_display_ = GetDisplayParams(composer_.get(),
- *displays.external_display, /*is_primary*/ false);
-
- ALOGI("External display connected. Switching to external display.");
- target_display_ = &(*external_display_);
- target_display_changed = true;
- } else {
- // External display was disconnected
- external_display_ = std::nullopt;
- if (was_using_external_display) {
- ALOGI("External display disconnected. Switching to primary display.");
- target_display_ = &primary_display_;
- target_display_changed = true;
- }
- }
- }
-
- if (target_display_changed) {
- // If we're switching to the external display, turn the primary display off.
- if (!target_display_->is_primary) {
- EnableDisplay(primary_display_, false);
- }
- // If we're switching to the primary display, and the external display is
- // still connected, turn the external display off.
- else if (target_display_->is_primary && external_display_) {
- EnableDisplay(*external_display_, false);
- }
-
- // Update the cached edid data for the current display.
- UpdateEdidData(composer_.get(), target_display_->id);
-
- // Turn the new target display on.
- EnableDisplay(*target_display_, true);
-
- // When we switch displays we need to recreate all the layers, so clear the
- // current list, which will trigger layer recreation.
- layers_.clear();
- }
-
- return target_display_changed;
-}
-
-// Checks for changes in the surface stack and updates the layer config to
-// accomodate the new stack.
-void HardwareComposer::UpdateLayerConfig() {
- std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces;
- {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
-
- if (!surfaces_changed_ && (!layers_.empty() || surfaces_.empty()))
- return;
-
- surfaces = surfaces_;
- surfaces_changed_ = false;
- }
-
- ATRACE_NAME("UpdateLayerConfig_HwLayers");
-
- // 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();
- });
-
- // 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;
-
- // 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(composer_.get(), *target_display_, surface, blending,
- HWC::Composition::Device, layer_index);
- }
-
- ALOGI_IF(
- TRACE,
- "HardwareComposer::UpdateLayerConfig: layer_index=%zu surface_id=%d",
- layer_index, layers[layer_index].GetSurfaceId());
-
- layer_index++;
- }
-
- // Sort the new layer stack by ascending surface id.
- std::sort(layers.begin(), layers.end());
-
- // 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());
-}
-
-std::vector<sp<IVsyncCallback>>::const_iterator
-HardwareComposer::VsyncService::FindCallback(
- const sp<IVsyncCallback>& callback) const {
- sp<IBinder> binder = IInterface::asBinder(callback);
- return std::find_if(callbacks_.cbegin(), callbacks_.cend(),
- [&](const sp<IVsyncCallback>& callback) {
- return IInterface::asBinder(callback) == binder;
- });
-}
-
-status_t HardwareComposer::VsyncService::registerCallback(
- const sp<IVsyncCallback> callback) {
- std::lock_guard<std::mutex> autolock(mutex_);
- if (FindCallback(callback) == callbacks_.cend()) {
- callbacks_.push_back(callback);
- }
- return OK;
-}
-
-status_t HardwareComposer::VsyncService::unregisterCallback(
- const sp<IVsyncCallback> callback) {
- std::lock_guard<std::mutex> autolock(mutex_);
- auto iter = FindCallback(callback);
- if (iter != callbacks_.cend()) {
- callbacks_.erase(iter);
- }
- return OK;
-}
-
-void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) {
- ATRACE_NAME("VsyncService::OnVsync");
- std::lock_guard<std::mutex> autolock(mutex_);
- for (auto iter = callbacks_.begin(); iter != callbacks_.end();) {
- if ((*iter)->onVsync(vsync_timestamp) == android::DEAD_OBJECT) {
- iter = callbacks_.erase(iter);
- } else {
- ++iter;
- }
- }
-}
-
-Return<void> HardwareComposer::ComposerCallback::onHotplug(
- Hwc2::Display display, IComposerCallback::Connection conn) {
- std::lock_guard<std::mutex> lock(mutex_);
- ALOGI("onHotplug display=%" PRIu64 " conn=%d", display, conn);
-
- bool is_primary = !got_first_hotplug_ || display == primary_display_.id;
-
- // Our first onHotplug callback is always for the primary display.
- if (!got_first_hotplug_) {
- LOG_ALWAYS_FATAL_IF(conn != IComposerCallback::Connection::CONNECTED,
- "Initial onHotplug callback should be primary display connected");
- got_first_hotplug_ = true;
- } else if (is_primary) {
- ALOGE("Ignoring unexpected onHotplug() call for primary display");
- return Void();
- }
-
- if (conn == IComposerCallback::Connection::CONNECTED) {
- if (!is_primary)
- external_display_ = DisplayInfo();
- DisplayInfo& display_info = is_primary ?
- primary_display_ : *external_display_;
- display_info.id = display;
-
- 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);
- display_info.driver_vsync_event_fd = std::move(handle);
- } else {
- ALOGI(
- "HardwareComposer::ComposerCallback::onHotplug: Driver does not "
- "support vsync_event node for display %" PRIu64,
- display);
- }
- } else if (conn == IComposerCallback::Connection::DISCONNECTED) {
- external_display_ = std::nullopt;
- }
-
- if (!is_primary)
- external_display_was_hotplugged_ = true;
-
- return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onRefresh(
- Hwc2::Display /*display*/) {
- return hardware::Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
- int64_t timestamp) {
- TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
- display, timestamp);
- std::lock_guard<std::mutex> lock(mutex_);
- DisplayInfo* display_info = GetDisplayInfo(display);
- if (display_info) {
- display_info->callback_vsync_timestamp = timestamp;
- }
- if (primary_display_.id == display && vsync_service_ != nullptr) {
- vsync_service_->OnVsync(timestamp);
- }
-
- return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsync_2_4(
- Hwc2::Display /*display*/, int64_t /*timestamp*/,
- Hwc2::VsyncPeriodNanos /*vsyncPeriodNanos*/) {
- LOG_ALWAYS_FATAL("Unexpected onVsync_2_4 callback");
- return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsyncPeriodTimingChanged(
- Hwc2::Display /*display*/,
- const Hwc2::VsyncPeriodChangeTimeline& /*updatedTimeline*/) {
- LOG_ALWAYS_FATAL("Unexpected onVsyncPeriodTimingChanged callback");
- return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onSeamlessPossible(
- Hwc2::Display /*display*/) {
- LOG_ALWAYS_FATAL("Unexpected onSeamlessPossible callback");
- return Void();
-}
-
-void HardwareComposer::ComposerCallback::SetVsyncService(
- const sp<VsyncService>& vsync_service) {
- std::lock_guard<std::mutex> lock(mutex_);
- vsync_service_ = vsync_service;
-}
-
-HardwareComposer::ComposerCallback::Displays
-HardwareComposer::ComposerCallback::GetDisplays() {
- std::lock_guard<std::mutex> lock(mutex_);
- Displays displays;
- displays.primary_display = primary_display_.id;
- if (external_display_)
- displays.external_display = external_display_->id;
- if (external_display_was_hotplugged_) {
- external_display_was_hotplugged_ = false;
- displays.external_display_was_hotplugged = true;
- }
- return displays;
-}
-
-Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
- hwc2_display_t display) {
- std::lock_guard<std::mutex> autolock(mutex_);
- DisplayInfo* display_info = GetDisplayInfo(display);
- if (!display_info) {
- ALOGW("Attempt to get vsync time for unknown display %" PRIu64, display);
- return ErrorStatus(EINVAL);
- }
-
- // See if the driver supports direct vsync events.
- LocalHandle& event_fd = display_info->driver_vsync_event_fd;
- if (!event_fd) {
- // Fall back to returning the last timestamp returned by the vsync
- // callback.
- return display_info->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*>(&timestamp));
- if (ret < 0) {
- const int error = errno;
- ALOGE(
- "HardwareComposer::ComposerCallback::GetVsyncTime: Error while "
- "parsing timestamp: %s",
- strerror(error));
- return ErrorStatus(error);
- }
-
- return {timestamp};
-}
-
-HardwareComposer::ComposerCallback::DisplayInfo*
-HardwareComposer::ComposerCallback::GetDisplayInfo(hwc2_display_t display) {
- if (display == primary_display_.id) {
- return &primary_display_;
- } else if (external_display_ && display == external_display_->id) {
- return &(*external_display_);
- }
- return nullptr;
-}
-
-void Layer::Reset() {
- if (hardware_composer_layer_) {
- HWC::Error error =
- composer_->destroyLayer(display_params_.id, hardware_composer_layer_);
- if (error != HWC::Error::None &&
- (!ignore_bad_display_errors_on_destroy_ ||
- error != HWC::Error::BadDisplay)) {
- ALOGE("destroyLayer() failed for display %" PRIu64 ", layer %" PRIu64
- ". error: %s", display_params_.id, hardware_composer_layer_,
- error.to_string().c_str());
- }
- hardware_composer_layer_ = 0;
- }
-
- z_order_ = 0;
- blending_ = HWC::BlendMode::None;
- composition_type_ = HWC::Composition::Invalid;
- target_composition_type_ = composition_type_;
- source_ = EmptyVariant{};
- acquire_fence_.Close();
- surface_rect_functions_applied_ = false;
- pending_visibility_settings_ = true;
- cached_buffer_map_.clear();
- ignore_bad_display_errors_on_destroy_ = false;
-}
-
-Layer::Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
- const std::shared_ptr<DirectDisplaySurface>& surface,
- HWC::BlendMode blending, HWC::Composition composition_type,
- size_t z_order)
- : composer_(composer),
- display_params_(display_params),
- z_order_{z_order},
- blending_{blending},
- target_composition_type_{composition_type},
- source_{SourceSurface{surface}} {
- CommonLayerSetup();
-}
-
-Layer::Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
- const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
- HWC::Composition composition_type, size_t z_order)
- : composer_(composer),
- display_params_(display_params),
- z_order_{z_order},
- blending_{blending},
- target_composition_type_{composition_type},
- source_{SourceBuffer{buffer}} {
- CommonLayerSetup();
-}
-
-Layer::~Layer() { Reset(); }
-
-Layer::Layer(Layer&& other) noexcept { *this = std::move(other); }
-
-Layer& Layer::operator=(Layer&& other) noexcept {
- if (this != &other) {
- Reset();
- using std::swap;
- swap(composer_, other.composer_);
- swap(display_params_, other.display_params_);
- swap(hardware_composer_layer_, other.hardware_composer_layer_);
- swap(z_order_, other.z_order_);
- swap(blending_, other.blending_);
- 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_);
- swap(ignore_bad_display_errors_on_destroy_,
- other.ignore_bad_display_errors_on_destroy_);
- }
- 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) {
- 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 {
- IonBuffer* operator()(SourceSurface& source) { return source.GetBuffer(); }
- IonBuffer* operator()(SourceBuffer& source) { return source.GetBuffer(); }
- IonBuffer* operator()(EmptyVariant) { return nullptr; }
- };
- return source_.Visit(Visitor{});
-}
-
-void Layer::UpdateVisibilitySettings() {
- if (pending_visibility_settings_) {
- pending_visibility_settings_ = false;
-
- HWC::Error error;
-
- error = composer_->setLayerBlendMode(
- display_params_.id, 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_params_.id,
- 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;
-
- UpdateVisibilitySettings();
-
- // TODO(eieio): Use surface attributes or some other mechanism to control
- // the layer display frame.
- error = composer_->setLayerDisplayFrame(
- display_params_.id, hardware_composer_layer_,
- {0, 0, display_params_.width, display_params_.height});
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer display frame: %s",
- error.to_string().c_str());
-
- error = composer_->setLayerVisibleRegion(
- display_params_.id, hardware_composer_layer_,
- {{0, 0, display_params_.width, display_params_.height}});
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer visible region: %s",
- error.to_string().c_str());
-
- error = composer_->setLayerPlaneAlpha(display_params_.id,
- hardware_composer_layer_, 1.0f);
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer plane alpha: %s",
- error.to_string().c_str());
-}
-
-void Layer::CommonLayerSetup() {
- HWC::Error error = composer_->createLayer(display_params_.id,
- &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, 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, id, handle, acquire_fence_, slot) =
- source.Acquire();
- });
-
- 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;
- composer_->setLayerCompositionType(
- display_params_.id, hardware_composer_layer_,
- composition_type_.cast<Hwc2::IComposerClient::Composition>());
- Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0};
- composer_->setLayerColor(display_params_.id, hardware_composer_layer_,
- layer_color);
- } else {
- // The composition type is already set. Nothing else to do until a
- // buffer arrives.
- }
- } else {
- if (composition_type_ != target_composition_type_) {
- composition_type_ = target_composition_type_;
- composer_->setLayerCompositionType(
- display_params_.id, 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 =
- composer_->setLayerBuffer(display_params_.id, hardware_composer_layer_,
- slot, handle, acquire_fence_.Get());
-
- ALOGE_IF(error != HWC::Error::None,
- "Layer::Prepare: Error setting layer buffer: %s",
- error.to_string().c_str());
-
- if (!surface_rect_functions_applied_) {
- const float float_right = right;
- const float float_bottom = bottom;
- error = composer_->setLayerSourceCrop(display_params_.id,
- hardware_composer_layer_,
- {0, 0, float_right, float_bottom});
-
- ALOGE_IF(error != HWC::Error::None,
- "Layer::Prepare: Error setting layer source crop: %s",
- error.to_string().c_str());
-
- surface_rect_functions_applied_ = true;
- }
- }
-}
-
-void Layer::Finish(int release_fence_fd) {
- IfAnyOf<SourceSurface, SourceBuffer>::Call(
- &source_, [release_fence_fd](auto& source) {
- source.Finish(LocalHandle(release_fence_fd));
- });
-}
-
-void Layer::Drop() { acquire_fence_.Close(); }
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
deleted file mode 100644
index bfce10b5b0..0000000000
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ /dev/null
@@ -1,577 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
-
-#include <ui/GraphicBuffer.h>
-#include "DisplayHardware/ComposerHal.h"
-#include "hwc_types.h"
-
-#include <dvr/dvr_shared_buffers.h>
-#include <hardware/gralloc.h>
-#include <log/log.h>
-
-#include <array>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <thread>
-#include <tuple>
-#include <vector>
-
-#include <dvr/dvr_config.h>
-#include <dvr/dvr_vsync.h>
-#include <pdx/file_handle.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/shared_buffer_helpers.h>
-#include <private/dvr/vsync_service.h>
-
-#include "DisplayHardware/DisplayIdentification.h"
-#include "acquired_buffer.h"
-#include "display_surface.h"
-
-// Hardware composer HAL doesn't define HWC_TRANSFORM_NONE as of this writing.
-#ifndef HWC_TRANSFORM_NONE
-#define HWC_TRANSFORM_NONE static_cast<hwc_transform_t>(0)
-#endif
-
-namespace android {
-namespace dvr {
-
-// Basic display metrics for physical displays.
-struct DisplayParams {
- hwc2_display_t id;
- bool is_primary;
-
- int width;
- int height;
-
- struct {
- int x;
- int y;
- } dpi;
-
- int vsync_period_ns;
-};
-
-// Layer represents the connection between a hardware composer layer and the
-// source supplying buffers for the layer's contents.
-class Layer {
- public:
- 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
- // every frame.
- //
- // |composer| The composer instance.
- // |display_params| Info about the display to use.
- // |blending| receives HWC_BLENDING_* values.
- // |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.
- Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
- const std::shared_ptr<DirectDisplaySurface>& surface,
- HWC::BlendMode blending, HWC::Composition composition_type,
- size_t z_order);
-
- // 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
- // changing the buffer each frame is on the caller.
- //
- // |composer| The composer instance.
- // |display_params| Info about the display to use.
- // |blending| receives HWC_BLENDING_* values.
- // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
- // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
- Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
- const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
- HWC::Composition composition_type, size_t z_order);
-
- Layer(Layer&&) noexcept;
- Layer& operator=(Layer&&) noexcept;
-
- ~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.
- void UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer);
-
- // Sets up the hardware composer layer for the next frame. When the layer is
- // associated with a display surface, this method automatically ACQUIRES a new
- // buffer if one is available.
- void Prepare();
-
- // After calling prepare, if this frame is to be dropped instead of passing
- // along to the HWC, call Drop to close the contained fence(s).
- void Drop();
-
- // Performs fence bookkeeping after the frame has been posted to hardware
- // composer.
- void Finish(int release_fence_fd);
-
- // Sets the blending for the layer. |blending| receives HWC_BLENDING_* values.
- void SetBlending(HWC::BlendMode blending);
-
- // Sets the z-order of this layer
- void SetZOrder(size_t z_order);
-
- // Gets the current IonBuffer associated with this layer. Ownership of the
- // buffer DOES NOT pass to the caller and the pointer is not guaranteed to
- // remain valid across calls to Layer::Setup(), Layer::Prepare(), or
- // Layer::Reset(). YOU HAVE BEEN WARNED.
- IonBuffer* GetBuffer();
-
- HWC::Composition GetCompositionType() const { return composition_type_; }
- HWC::Layer GetLayerHandle() const { return hardware_composer_layer_; }
- bool IsLayerSetup() const { return !source_.empty(); }
-
- int GetSurfaceId() const {
- int surface_id = -1;
- pdx::rpc::IfAnyOf<SourceSurface>::Call(
- &source_, [&surface_id](const SourceSurface& surface_source) {
- surface_id = surface_source.GetSurfaceId();
- });
- return surface_id;
- }
-
- int GetBufferId() const {
- int buffer_id = -1;
- pdx::rpc::IfAnyOf<SourceSurface>::Call(
- &source_, [&buffer_id](const SourceSurface& surface_source) {
- buffer_id = surface_source.GetBufferId();
- });
- 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; }
-
- void IgnoreBadDisplayErrorsOnDestroy(bool ignore) {
- ignore_bad_display_errors_on_destroy_ = ignore;
- }
-
- private:
- void CommonLayerSetup();
-
- // 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.
- Hwc2::Composer* composer_ = nullptr;
-
- // Parameters of the display to use for this layer.
- DisplayParams display_params_;
-
- // The hardware composer layer and metrics to use during the prepare cycle.
- hwc2_layer_t hardware_composer_layer_ = 0;
-
- // Layer properties used to setup the hardware composer layer during the
- // Prepare phase.
- size_t z_order_ = 0;
- HWC::BlendMode blending_ = HWC::BlendMode::None;
- HWC::Composition composition_type_ = HWC::Composition::Invalid;
- HWC::Composition target_composition_type_ = HWC::Composition::Device;
-
- // State when the layer is connected to a surface. Provides the same interface
- // as SourceBuffer to simplify internal use by Layer.
- struct SourceSurface {
- std::shared_ptr<DirectDisplaySurface> surface;
- AcquiredBuffer acquired_buffer;
- pdx::LocalHandle release_fence;
-
- explicit SourceSurface(const std::shared_ptr<DirectDisplaySurface>& surface)
- : surface(surface) {}
-
- // Attempts to acquire a new buffer from the surface and return a tuple with
- // width, height, buffer handle, and fence. If a new buffer is not available
- // 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, 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()->id(),
- acquired_buffer.buffer()->buffer()->buffer(),
- acquired_buffer.ClaimAcquireFence(), acquired_buffer.slot());
- } else {
- return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
- }
- }
-
- void Finish(pdx::LocalHandle fence) { release_fence = std::move(fence); }
-
- // Gets a pointer to the current acquired buffer or returns nullptr if there
- // isn't one.
- IonBuffer* GetBuffer() {
- if (acquired_buffer.IsAvailable())
- return acquired_buffer.buffer()->buffer();
- else
- return nullptr;
- }
-
- // Returns the surface id of the surface.
- int GetSurfaceId() const { return surface->surface_id(); }
-
- // Returns the buffer id for the current buffer.
- int GetBufferId() const {
- if (acquired_buffer.IsAvailable())
- return acquired_buffer.buffer()->id();
- else
- return -1;
- }
- };
-
- // State when the layer is connected to a buffer. Provides the same interface
- // as SourceSurface to simplify internal use by Layer.
- struct SourceBuffer {
- std::shared_ptr<IonBuffer> buffer;
-
- std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
- Acquire() {
- if (buffer)
- return std::make_tuple(buffer->width(), buffer->height(), -1,
- buffer->buffer(), pdx::LocalHandle{}, 0);
- else
- return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
- }
-
- void Finish(pdx::LocalHandle /*fence*/) {}
-
- IonBuffer* GetBuffer() { return buffer.get(); }
-
- int GetSurfaceId() const { return -1; }
- int GetBufferId() const { return -1; }
- };
-
- // The underlying hardware composer layer is supplied buffers either from a
- // surface buffer train or from a buffer directly.
- pdx::rpc::Variant<SourceSurface, SourceBuffer> source_;
-
- 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_;
-
- // When calling destroyLayer() on an external display that's been removed we
- // typically get HWC2_ERROR_BAD_DISPLAY errors. If
- // ignore_bad_display_errors_on_destroy_ is true, don't log the bad display
- // errors, since they're expected.
- bool ignore_bad_display_errors_on_destroy_ = false;
-
- Layer(const Layer&) = delete;
- void operator=(const Layer&) = delete;
-};
-
-// HardwareComposer encapsulates the hardware composer HAL, exposing a
-// simplified API to post buffers to the display.
-//
-// HardwareComposer is accessed by both the vr flinger dispatcher thread and the
-// surface flinger main thread, in addition to internally running a separate
-// thread for compositing/EDS and posting layers to the HAL. When changing how
-// variables are used or adding new state think carefully about which threads
-// will access the state and whether it needs to be synchronized.
-class HardwareComposer {
- public:
- using RequestDisplayCallback = std::function<void(bool)>;
-
- HardwareComposer();
- ~HardwareComposer();
-
- bool Initialize(Hwc2::Composer* composer,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback);
-
- bool IsInitialized() const { return initialized_; }
-
- // Start the post thread if there's work to do (i.e. visible layers). This
- // should only be called from surface flinger's main thread.
- void Enable();
- // Pause the post thread, blocking until the post thread has signaled that
- // it's paused. This should only be called from surface flinger's main thread.
- void Disable();
-
- // Called on a binder thread.
- void OnBootFinished();
-
- std::string Dump();
-
- const DisplayParams& GetPrimaryDisplayParams() const {
- return primary_display_;
- }
-
- // Sets the display surfaces to compose the hardware layer stack.
- void SetDisplaySurfaces(
- std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces);
-
- int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer);
- void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
-
- // Gets the edid data for the current active display (internal or external)
- DisplayIdentificationData GetCurrentDisplayIdentificationData() {
- return display_identification_data_;
- }
-
- // Gets the edid port for the current active display (internal or external)
- uint8_t GetCurrentDisplayPort() { return display_port_; }
-
- private:
- DisplayParams GetDisplayParams(Hwc2::Composer* composer,
- hwc2_display_t display, bool is_primary);
-
- // Turn display vsync on/off. Returns true on success, false on failure.
- bool EnableVsync(const DisplayParams& display, bool enabled);
- // Turn display power on/off. Returns true on success, false on failure.
- bool SetPowerMode(const DisplayParams& display, bool active);
- // Convenience function to turn a display on/off. Turns both power and vsync
- // on/off. Returns true on success, false on failure.
- bool EnableDisplay(const DisplayParams& display, bool enabled);
-
- class VsyncService : public BnVsyncService {
- public:
- status_t registerCallback(const sp<IVsyncCallback> callback) override;
- status_t unregisterCallback(const sp<IVsyncCallback> callback) override;
- void OnVsync(int64_t vsync_timestamp);
- private:
- std::vector<sp<IVsyncCallback>>::const_iterator FindCallback(
- const sp<IVsyncCallback>& callback) const;
- std::mutex mutex_;
- std::vector<sp<IVsyncCallback>> callbacks_;
- };
-
- class ComposerCallback : public Hwc2::IComposerCallback {
- public:
- 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> onVsync_2_4(
- Hwc2::Display display, int64_t timestamp,
- Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override;
- hardware::Return<void> onVsyncPeriodTimingChanged(
- Hwc2::Display display,
- const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override;
- hardware::Return<void> onSeamlessPossible(Hwc2::Display display) override;
-
- bool GotFirstHotplug() { return got_first_hotplug_; }
- void SetVsyncService(const sp<VsyncService>& vsync_service);
-
- struct Displays {
- hwc2_display_t primary_display = 0;
- std::optional<hwc2_display_t> external_display;
- bool external_display_was_hotplugged = false;
- };
-
- Displays GetDisplays();
- pdx::Status<int64_t> GetVsyncTime(hwc2_display_t display);
-
- private:
- struct DisplayInfo {
- hwc2_display_t id = 0;
- pdx::LocalHandle driver_vsync_event_fd;
- int64_t callback_vsync_timestamp{0};
- };
-
- DisplayInfo* GetDisplayInfo(hwc2_display_t display);
-
- std::mutex mutex_;
-
- bool got_first_hotplug_ = false;
- DisplayInfo primary_display_;
- std::optional<DisplayInfo> external_display_;
- bool external_display_was_hotplugged_ = false;
- sp<VsyncService> vsync_service_;
- };
-
- HWC::Error Validate(hwc2_display_t display);
- HWC::Error Present(hwc2_display_t display);
-
- void PostLayers(hwc2_display_t display);
- void PostThread();
-
- // The post thread has two controlling states:
- // 1. Idle: no work to do (no visible surfaces).
- // 2. Suspended: explicitly halted (system is not in VR mode).
- // When either #1 or #2 is true then the post thread is quiescent, otherwise
- // it is active.
- using PostThreadStateType = uint32_t;
- struct PostThreadState {
- enum : PostThreadStateType {
- Active = 0,
- Idle = (1 << 0),
- Suspended = (1 << 1),
- Quit = (1 << 2),
- };
- };
-
- void UpdatePostThreadState(uint32_t state, bool suspend);
-
- // Blocks until either event_fd becomes readable, or we're interrupted by a
- // 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 timeout_ms);
-
- // WaitForPredictedVSync 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();
- pdx::Status<int64_t> WaitForPredictedVSync();
- int SleepUntil(int64_t wakeup_timestamp);
-
- // Initialize any newly connected displays, and set target_display_ to the
- // display we should render to. Returns true if target_display_
- // changed. Called only from the post thread.
- bool UpdateTargetDisplay();
-
- // Reconfigures the layer stack if the display surfaces changed since the last
- // frame. Called only from the post thread.
- void UpdateLayerConfig();
-
- // Called on the post thread to create the Composer instance.
- void CreateComposer();
-
- // Called on the post thread when the post thread is resumed.
- void OnPostThreadResumed();
- // Called on the post thread when the post thread is paused or quits.
- void OnPostThreadPaused();
-
- // Use post_thread_wait_ to wait for a specific condition, specified by pred.
- // timeout_sec < 0 means wait indefinitely, otherwise it specifies the timeout
- // in seconds.
- // The lock must be held when this function is called.
- // Returns true if the wait was interrupted because the post thread was asked
- // to quit.
- bool PostThreadCondWait(std::unique_lock<std::mutex>& lock,
- int timeout_sec,
- const std::function<bool()>& pred);
-
- // Map the given shared memory buffer to our broadcast ring to track updates
- // to the config parameters.
- int MapConfigBuffer(IonBuffer& ion_buffer);
- void ConfigBufferDeleted();
- // Poll for config udpates.
- void UpdateConfigBuffer();
-
- bool initialized_;
- bool is_standalone_device_;
-
- std::unique_ptr<Hwc2::Composer> composer_;
- sp<ComposerCallback> composer_callback_;
- RequestDisplayCallback request_display_callback_;
-
- DisplayParams primary_display_;
- std::optional<DisplayParams> external_display_;
- DisplayParams* target_display_ = &primary_display_;
-
- // The list of surfaces we should draw. Set by the display service when
- // DirectSurfaces are added, removed, or change visibility. Written by the
- // message dispatch thread and read by the post thread.
- std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces_;
- // Set to true by the dispatch thread whenever surfaces_ changes. Set to false
- // by the post thread when the new list of surfaces is processed.
- bool surfaces_changed_ = false;
-
- std::vector<std::shared_ptr<DirectDisplaySurface>> current_surfaces_;
-
- // 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_;
-
- // The layer posting thread. This thread wakes up a short time before vsync to
- // hand buffers to hardware composer.
- std::thread post_thread_;
-
- // Post thread state machine and synchronization primitives.
- 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_;
- std::mutex post_thread_mutex_;
- std::condition_variable post_thread_wait_;
- std::condition_variable post_thread_ready_;
-
- // When boot is finished this will be set to true and the post thread will be
- // notified via post_thread_wait_.
- bool boot_finished_ = false;
-
- // 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;
-
- // Counter tracking the number of skipped frames.
- int frame_skip_count_ = 0;
-
- // Fd array for tracking retire fences that are returned by hwc. This allows
- // us to detect when the display driver begins queuing frames.
- std::vector<pdx::LocalHandle> retire_fence_fds_;
-
- // If we are publishing vsync data, we will put it here.
- std::unique_ptr<CPUMappedBroadcastRing<DvrVsyncRing>> vsync_ring_;
-
- // Broadcast ring for receiving config data from the DisplayManager.
- DvrConfigRing shared_config_ring_;
- uint32_t shared_config_ring_sequence_{0};
- // Config buffer for reading from the post thread.
- DvrConfig post_thread_config_;
- std::mutex shared_config_mutex_;
-
- bool vsync_trace_parity_ = false;
- sp<VsyncService> vsync_service_;
-
- // Edid section.
- void UpdateEdidData(Hwc2::Composer* composer, hwc2_display_t hw_id);
- DisplayIdentificationData display_identification_data_;
- uint8_t display_port_;
-
- static constexpr int kPostThreadInterrupted = 1;
-
- HardwareComposer(const HardwareComposer&) = delete;
- void operator=(const HardwareComposer&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
diff --git a/libs/vr/libvrflinger/hwc_types.h b/libs/vr/libvrflinger/hwc_types.h
deleted file mode 100644
index 8b5c3b3e79..0000000000
--- a/libs/vr/libvrflinger/hwc_types.h
+++ /dev/null
@@ -1,307 +0,0 @@
-#ifndef ANDROID_LIBVRFLINGER_HWCTYPES_H
-#define ANDROID_LIBVRFLINGER_HWCTYPES_H
-
-// General HWC type support. Hardware composer type support is a bit of a mess
-// between HWC1, HWC2 C/C++11, and HIDL types. Particularly bothersome is the
-// use of enum classes, which make analogous types between versions much
-// harder to deal with in a uniform way.
-//
-// These utilities help address some of these pains by providing a type-safe,
-// flexible interface to translate between different type spaces.
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include <string>
-#include <type_traits>
-
-namespace HWC {
-
-// Value types derived from HWC HAL types. Some of these are stand-alone,
-// while others are also wrapped in translator classes below.
-using ColorMode = int32_t; // android_color_mode_t;
-using Config = hwc2_config_t;
-using ColorTransform =
- std::underlying_type<android_color_transform_t>::type; // int32_t;
-using Dataspace = std::underlying_type<android_dataspace_t>::type; // int32_t;
-using DisplayId = hwc2_display_t;
-using DisplayRequest = std::underlying_type<HWC2::DisplayRequest>::type;
-using Hdr = std::underlying_type<android_hdr_t>::type; // int32_t;
-using Layer = hwc2_layer_t;
-using PixelFormat =
- std::underlying_type<android_pixel_format_t>::type; // int32_t;
-
-// Type traits and casting utilities.
-
-// SFINAE utility to evaluate type expressions.
-template <typename...>
-using TestTypeExpression = void;
-
-// Traits type to determine the underlying type of an enum, integer,
-// or wrapper class.
-template <typename T, typename = typename std::is_enum<T>::type,
- typename = typename std::is_integral<T>::type, typename = void>
-struct UnderlyingType {
- using Type = T;
-};
-// Partial specialization that matches enum types. Captures the underlying type
-// of the enum in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::true_type, std::false_type> {
- using Type = typename std::underlying_type<T>::type;
-};
-// Partial specialization that matches integral types. Captures the type of the
-// integer in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::false_type, std::true_type> {
- using Type = T;
-};
-// Partial specialization that matches the wrapper types below. Captures
-// wrapper member type ValueType in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::false_type, std::false_type,
- TestTypeExpression<typename T::ValueType>> {
- using Type = typename T::ValueType;
-};
-
-// Enable if T is an enum with underlying type U.
-template <typename T, typename U, typename ReturnType = void>
-using EnableIfMatchingEnum = typename std::enable_if<
- std::is_enum<T>::value &&
- std::is_same<U, typename UnderlyingType<T>::Type>::value,
- ReturnType>::type;
-
-// Enable if T and U are the same size/alignment and have the same underlying
-// type. Handles enum, integral, and wrapper classes below.
-template <typename T, typename U, typename Return = void>
-using EnableIfSafeCast = typename std::enable_if<
- sizeof(T) == sizeof(U) && alignof(T) == alignof(U) &&
- std::is_same<typename UnderlyingType<T>::Type,
- typename UnderlyingType<U>::Type>::value,
- Return>::type;
-
-// Safely cast between std::vectors of matching enum/integer/wraper types.
-// Normally this is not possible with pendantic compiler type checks. However,
-// given the same size, alignment, and underlying type this is safe due to
-// allocator requirements and array-like element access guarantees.
-template <typename T, typename U>
-EnableIfSafeCast<T, U, std::vector<T>*> VectorCast(std::vector<U>* in) {
- return reinterpret_cast<std::vector<T>*>(in);
-}
-
-// Translator classes that wrap specific HWC types to make translating
-// between different types (especially enum class) in code cleaner.
-
-// Base type for the enum wrappers below. This type provides type definitions
-// and implicit conversion logic common to each wrapper type.
-template <typename EnumType>
-struct Wrapper {
- // Alias type of this instantiantion of Wrapper. Useful for inheriting
- // constructors in subclasses via "using Base::Base;" statements.
- using Base = Wrapper<EnumType>;
-
- // The enum type wrapped by this instantiation of Wrapper.
- using BaseType = EnumType;
-
- // The underlying type of the base enum type.
- using ValueType = typename UnderlyingType<BaseType>::Type;
-
- // A default constructor is not defined here. Subclasses should define one
- // as appropriate to define the correct inital value for the enum type.
-
- // Default copy constructor.
- Wrapper(const Wrapper&) = default;
-
- // Implicit conversion from ValueType.
- // NOLINTNEXTLINE(google-explicit-constructor)
- Wrapper(ValueType value) : value(value) {}
-
- // Implicit conversion from BaseType.
- // NOLINTNEXTLINE(google-explicit-constructor)
- Wrapper(BaseType value) : value(static_cast<ValueType>(value)) {}
-
- // Implicit conversion from an enum type of the same underlying type.
- template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
- // NOLINTNEXTLINE(google-explicit-constructor)
- Wrapper(const T& value) : value(static_cast<ValueType>(value)) {}
-
- // Implicit conversion to BaseType.
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator BaseType() const { return static_cast<BaseType>(value); }
-
- // Implicit conversion to ValueType.
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator ValueType() const { return value; }
-
- template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
- T cast() const {
- return static_cast<T>(value);
- }
-
- // Converts to string using HWC2 stringification of BaseType.
- std::string to_string() const {
- return HWC2::to_string(static_cast<BaseType>(value));
- }
-
- bool operator!=(const Wrapper& other) const { return value != other.value; }
- bool operator!=(ValueType other_value) const { return value != other_value; }
- bool operator!=(BaseType other_value) const {
- return static_cast<BaseType>(value) != other_value;
- }
- bool operator==(const Wrapper& other) const { return value == other.value; }
- bool operator==(ValueType other_value) const { return value == other_value; }
- bool operator==(BaseType other_value) const {
- return static_cast<BaseType>(value) == other_value;
- }
-
- ValueType value;
-};
-
-struct Attribute final : public Wrapper<HWC2::Attribute> {
- enum : ValueType {
- Invalid = HWC2_ATTRIBUTE_INVALID,
- Width = HWC2_ATTRIBUTE_WIDTH,
- Height = HWC2_ATTRIBUTE_HEIGHT,
- VsyncPeriod = HWC2_ATTRIBUTE_VSYNC_PERIOD,
- DpiX = HWC2_ATTRIBUTE_DPI_X,
- DpiY = HWC2_ATTRIBUTE_DPI_Y,
- };
-
- Attribute() : Base(Invalid) {}
- using Base::Base;
-};
-
-struct BlendMode final : public Wrapper<HWC2::BlendMode> {
- enum : ValueType {
- Invalid = HWC2_BLEND_MODE_INVALID,
- None = HWC2_BLEND_MODE_NONE,
- Premultiplied = HWC2_BLEND_MODE_PREMULTIPLIED,
- Coverage = HWC2_BLEND_MODE_COVERAGE,
- };
-
- BlendMode() : Base(Invalid) {}
- using Base::Base;
-};
-
-struct Composition final : public Wrapper<HWC2::Composition> {
- enum : ValueType {
- Invalid = HWC2_COMPOSITION_INVALID,
- Client = HWC2_COMPOSITION_CLIENT,
- Device = HWC2_COMPOSITION_DEVICE,
- SolidColor = HWC2_COMPOSITION_SOLID_COLOR,
- Cursor = HWC2_COMPOSITION_CURSOR,
- Sideband = HWC2_COMPOSITION_SIDEBAND,
- };
-
- Composition() : Base(Invalid) {}
- using Base::Base;
-};
-
-struct DisplayType final : public Wrapper<HWC2::DisplayType> {
- enum : ValueType {
- Invalid = HWC2_DISPLAY_TYPE_INVALID,
- Physical = HWC2_DISPLAY_TYPE_PHYSICAL,
- Virtual = HWC2_DISPLAY_TYPE_VIRTUAL,
- };
-
- DisplayType() : Base(Invalid) {}
- using Base::Base;
-};
-
-struct Error final : public Wrapper<HWC2::Error> {
- enum : ValueType {
- None = HWC2_ERROR_NONE,
- BadConfig = HWC2_ERROR_BAD_CONFIG,
- BadDisplay = HWC2_ERROR_BAD_DISPLAY,
- BadLayer = HWC2_ERROR_BAD_LAYER,
- BadParameter = HWC2_ERROR_BAD_PARAMETER,
- HasChanges = HWC2_ERROR_HAS_CHANGES,
- NoResources = HWC2_ERROR_NO_RESOURCES,
- NotValidated = HWC2_ERROR_NOT_VALIDATED,
- Unsupported = HWC2_ERROR_UNSUPPORTED,
- };
-
- Error() : Base(None) {}
- using Base::Base;
-};
-
-struct LayerRequest final : public Wrapper<HWC2::LayerRequest> {
- enum : ValueType {
- ClearClientTarget = HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET,
- };
-
- LayerRequest() : Base(0) {}
- using Base::Base;
-};
-
-struct PowerMode final : public Wrapper<HWC2::PowerMode> {
- enum : ValueType {
- Off = HWC2_POWER_MODE_OFF,
- DozeSuspend = HWC2_POWER_MODE_DOZE_SUSPEND,
- Doze = HWC2_POWER_MODE_DOZE,
- On = HWC2_POWER_MODE_ON,
- };
-
- PowerMode() : Base(Off) {}
- using Base::Base;
-};
-
-struct Transform final : public Wrapper<HWC2::Transform> {
- enum : ValueType {
- None = 0,
- FlipH = HWC_TRANSFORM_FLIP_H,
- FlipV = HWC_TRANSFORM_FLIP_V,
- Rotate90 = HWC_TRANSFORM_ROT_90,
- Rotate180 = HWC_TRANSFORM_ROT_180,
- Rotate270 = HWC_TRANSFORM_ROT_270,
- FlipHRotate90 = HWC_TRANSFORM_FLIP_H_ROT_90,
- FlipVRotate90 = HWC_TRANSFORM_FLIP_V_ROT_90,
- };
-
- Transform() : Base(None) {}
- using Base::Base;
-};
-
-struct Vsync final : public Wrapper<HWC2::Vsync> {
- enum : ValueType {
- Invalid = HWC2_VSYNC_INVALID,
- Enable = HWC2_VSYNC_ENABLE,
- Disable = HWC2_VSYNC_DISABLE,
- };
-
- Vsync() : Base(Invalid) {}
- using Base::Base;
-};
-
-// Utility color type.
-struct Color final {
- Color(const Color&) = default;
- Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- Color(hwc_color_t color) : r(color.r), g(color.g), b(color.b), a(color.a) {}
-
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator hwc_color_t() const { return {r, g, b, a}; }
-
- uint8_t r __attribute__((aligned(1)));
- uint8_t g __attribute__((aligned(1)));
- uint8_t b __attribute__((aligned(1)));
- uint8_t a __attribute__((aligned(1)));
-};
-
-// Utility rectangle type.
-struct Rect final {
- // TODO(eieio): Implicit conversion to/from Android rect types.
-
- int32_t left __attribute__((aligned(4)));
- int32_t top __attribute__((aligned(4)));
- int32_t right __attribute__((aligned(4)));
- int32_t bottom __attribute__((aligned(4)));
-};
-
-} // namespace HWC
-
-#endif // ANDROID_LIBVRFLINGER_HWCTYPES_H
diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
deleted file mode 100644
index ae52076f99..0000000000
--- a/libs/vr/libvrflinger/include/dvr/vr_flinger.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef ANDROID_DVR_VR_FLINGER_H_
-#define ANDROID_DVR_VR_FLINGER_H_
-
-#include <thread>
-#include <memory>
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include <pdx/service_dispatcher.h>
-#include <vr/vr_manager/vr_manager.h>
-
-namespace android {
-
-namespace Hwc2 {
-class Composer;
-} // namespace Hwc2
-
-namespace dvr {
-
-class DisplayService;
-
-class VrFlinger {
- public:
- using RequestDisplayCallback = std::function<void(bool)>;
- static std::unique_ptr<VrFlinger> Create(
- Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback);
- ~VrFlinger();
-
- // These functions are all called on surface flinger's main thread.
- void OnBootFinished();
- void GrantDisplayOwnership();
- void SeizeDisplayOwnership();
-
- // dump all vr flinger state.
- std::string Dump();
-
- private:
- VrFlinger();
- bool Init(Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback);
-
- // Needs to be a separate class for binder's ref counting
- class PersistentVrStateCallback : public BnPersistentVrStateCallbacks {
- public:
- explicit PersistentVrStateCallback(
- RequestDisplayCallback request_display_callback)
- : request_display_callback_(request_display_callback) {}
- void onPersistentVrStateChanged(bool enabled) override;
- private:
- RequestDisplayCallback request_display_callback_;
- };
-
- std::thread dispatcher_thread_;
- std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher_;
- std::shared_ptr<android::dvr::DisplayService> display_service_;
- sp<PersistentVrStateCallback> persistent_vr_state_callback_;
- RequestDisplayCallback request_display_callback_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_VR_FLINGER_H_
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
deleted file mode 100644
index 095f556609..0000000000
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ /dev/null
@@ -1,47 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-shared_libs = [
- "android.hardware.configstore-utils",
- "android.hardware.configstore@1.0",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libgui",
- "libhidlbase",
- "liblog",
- "libui",
- "libutils",
- "libnativewindow",
- "libpdx_default_transport",
- "libSurfaceFlingerProp",
-]
-
-static_libs = [
- "libdisplay",
-]
-
-cc_test {
- srcs: ["vrflinger_test.cpp"],
- // See go/apct-presubmit for documentation on how this .filter file is used
- // by Android's automated testing infrastructure for test filtering.
- data: ["vrflinger_test.filter"],
- static_libs: static_libs,
- shared_libs: shared_libs,
- cflags: [
- "-DLOG_TAG=\"VrFlingerTest\"",
- "-DTRACE=0",
- "-O0",
- "-g",
- "-Wall",
- "-Werror",
- ],
- header_libs: ["libsurfaceflinger_headers"],
- name: "vrflinger_test",
-}
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
deleted file mode 100644
index ac44f74151..0000000000
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-#include <SurfaceFlingerProperties.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware_buffer.h>
-#include <binder/IServiceManager.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-#include <configstore/Utils.h>
-#include <cutils/properties.h>
-#include <gtest/gtest.h>
-#include <gui/ISurfaceComposer.h>
-#include <log/log.h>
-#include <utils/StrongPointer.h>
-
-#include <chrono>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <thread>
-
-#include <private/dvr/display_client.h>
-
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-using android::dvr::display::DisplayClient;
-using android::dvr::display::Surface;
-using android::dvr::display::SurfaceAttribute;
-using android::dvr::display::SurfaceAttributeValue;
-
-namespace android {
-namespace dvr {
-
-// The transaction code for asking surface flinger if vr flinger is active. This
-// is done as a hidden api since it's only used for tests. See the "case 1028"
-// block in SurfaceFlinger::onTransact() in SurfaceFlinger.cpp.
-constexpr uint32_t kIsVrFlingerActiveTransactionCode = 1028;
-
-// The maximum amount of time to give vr flinger to activate/deactivate. If the
-// switch hasn't completed in this amount of time, the test will fail.
-constexpr auto kVrFlingerSwitchMaxTime = std::chrono::seconds(1);
-
-// How long to wait between each check to see if the vr flinger switch
-// completed.
-constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);
-
-// A Binder connection to surface flinger.
-class SurfaceFlingerConnection {
- public:
- static std::unique_ptr<SurfaceFlingerConnection> Create() {
- sp<ISurfaceComposer> surface_flinger = interface_cast<ISurfaceComposer>(
- defaultServiceManager()->getService(String16("SurfaceFlinger")));
- if (surface_flinger == nullptr) {
- return nullptr;
- }
-
- return std::unique_ptr<SurfaceFlingerConnection>(
- new SurfaceFlingerConnection(surface_flinger));
- }
-
- // Returns true if the surface flinger process is still running. We use this
- // to detect if surface flinger has crashed.
- bool IsAlive() {
- IInterface::asBinder(surface_flinger_)->pingBinder();
- return IInterface::asBinder(surface_flinger_)->isBinderAlive();
- }
-
- // Return true if vr flinger is currently active, false otherwise. If there's
- // an error communicating with surface flinger, std::nullopt is returned.
- std::optional<bool> IsVrFlingerActive() {
- Parcel data, reply;
- status_t result =
- data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor());
- if (result != OK) {
- return std::nullopt;
- }
- result = IInterface::asBinder(surface_flinger_)
- ->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
- if (result != OK) {
- return std::nullopt;
- }
- bool vr_flinger_active;
- result = reply.readBool(&vr_flinger_active);
- if (result != OK) {
- return std::nullopt;
- }
- return vr_flinger_active;
- }
-
- enum class VrFlingerSwitchResult : int8_t {
- kSuccess,
- kTimedOut,
- kCommunicationError,
- kSurfaceFlingerDied
- };
-
- // Wait for vr flinger to become active or inactive.
- VrFlingerSwitchResult WaitForVrFlinger(bool wait_active) {
- return WaitForVrFlingerTimed(wait_active, kVrFlingerSwitchPollInterval,
- kVrFlingerSwitchMaxTime);
- }
-
- // Wait for vr flinger to become active or inactive, specifying custom timeouts.
- VrFlingerSwitchResult WaitForVrFlingerTimed(bool wait_active,
- std::chrono::milliseconds pollInterval, std::chrono::seconds timeout) {
- auto start_time = std::chrono::steady_clock::now();
- while (1) {
- std::this_thread::sleep_for(pollInterval);
- if (!IsAlive()) {
- return VrFlingerSwitchResult::kSurfaceFlingerDied;
- }
- std::optional<bool> vr_flinger_active = IsVrFlingerActive();
- if (!vr_flinger_active.has_value()) {
- return VrFlingerSwitchResult::kCommunicationError;
- }
- if (vr_flinger_active.value() == wait_active) {
- return VrFlingerSwitchResult::kSuccess;
- } else if (std::chrono::steady_clock::now() - start_time > timeout) {
- return VrFlingerSwitchResult::kTimedOut;
- }
- }
- }
-
- private:
- SurfaceFlingerConnection(sp<ISurfaceComposer> surface_flinger)
- : surface_flinger_(surface_flinger) {}
-
- sp<ISurfaceComposer> surface_flinger_ = nullptr;
-};
-
-// This test activates vr flinger by creating a vr flinger surface, then
-// deactivates vr flinger by destroying the surface. We verify that vr flinger
-// is activated and deactivated as expected, and that surface flinger doesn't
-// crash.
-//
-// If the device doesn't support vr flinger (as repoted by ConfigStore), the
-// test does nothing.
-//
-// If the device is a standalone vr device, the test also does nothing, since
-// this test verifies the behavior of display handoff from surface flinger to vr
-// flinger and back, and standalone devices never hand control of the display
-// back to surface flinger.
-TEST(VrFlingerTest, ActivateDeactivate) {
- android::ProcessState::self()->startThreadPool();
-
- // Exit immediately if the device doesn't support vr flinger. This ConfigStore
- // check is the same mechanism used by surface flinger to decide if it should
- // initialize vr flinger.
- bool vr_flinger_enabled = android::sysprop::use_vr_flinger(false);
- if (!vr_flinger_enabled) {
- return;
- }
-
- auto surface_flinger_connection = SurfaceFlingerConnection::Create();
- ASSERT_NE(surface_flinger_connection, nullptr);
-
- // Verify we start off with vr flinger disabled.
- ASSERT_TRUE(surface_flinger_connection->IsAlive());
- auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
- ASSERT_TRUE(vr_flinger_active.has_value());
- ASSERT_FALSE(vr_flinger_active.value());
-
- // Create a vr flinger surface, and verify vr flinger becomes active.
- // Introduce a scope so that, at the end of the scope, the vr flinger surface
- // is destroyed, and vr flinger deactivates.
- {
- auto display_client = DisplayClient::Create();
- ASSERT_NE(display_client, nullptr);
- auto metrics = display_client->GetDisplayMetrics();
- ASSERT_TRUE(metrics.ok());
-
- auto surface = Surface::CreateSurface({
- {SurfaceAttribute::Direct, SurfaceAttributeValue(true)},
- {SurfaceAttribute::Visible, SurfaceAttributeValue(true)},
- });
- ASSERT_TRUE(surface.ok());
- ASSERT_TRUE(surface.get() != nullptr);
-
- auto queue = surface.get()->CreateQueue(
- metrics.get().display_width, metrics.get().display_height,
- /*layer_count=*/1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
- AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
- AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
- /*capacity=*/1,
- /*metadata_size=*/0);
- ASSERT_TRUE(queue.ok());
- ASSERT_TRUE(queue.get() != nullptr);
-
- size_t slot;
- pdx::LocalHandle release_fence;
- auto buffer = queue.get()->Dequeue(/*timeout=*/0, &slot, &release_fence);
- ASSERT_TRUE(buffer.ok());
- ASSERT_TRUE(buffer.get() != nullptr);
-
- ASSERT_EQ(buffer.get()->width(), metrics.get().display_width);
- ASSERT_EQ(buffer.get()->height(), metrics.get().display_height);
-
- void* raw_buf = nullptr;
- ASSERT_GE(buffer.get()->buffer()->Lock(
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, /*x=*/0, /*y=*/0,
- buffer.get()->width(), buffer.get()->height(), &raw_buf),
- 0);
- ASSERT_NE(raw_buf, nullptr);
- uint32_t* pixels = static_cast<uint32_t*>(raw_buf);
-
- for (int i = 0; i < buffer.get()->stride() * buffer.get()->height(); ++i) {
- pixels[i] = 0x0000ff00;
- }
-
- ASSERT_GE(buffer.get()->buffer()->Unlock(), 0);
-
- ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle()), 0);
-
- ASSERT_EQ(
- surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/true),
- SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
- }
-
- // Now that the vr flinger surface is destroyed, vr flinger should deactivate.
- ASSERT_EQ(
- surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/false),
- SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.filter b/libs/vr/libvrflinger/tests/vrflinger_test.filter
deleted file mode 100644
index 030bb7b67c..0000000000
--- a/libs/vr/libvrflinger/tests/vrflinger_test.filter
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "presubmit": {
- "filter": "BootVrFlingerTest.*"
- }
-}
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
deleted file mode 100644
index a8a847664f..0000000000
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-#include <dvr/vr_flinger.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <memory>
-
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-#include <private/dvr/display_client.h>
-#include <processgroup/sched_policy.h>
-#include <sys/prctl.h>
-#include <sys/resource.h>
-
-#include <functional>
-
-#include "DisplayHardware/ComposerHal.h"
-#include "display_manager_service.h"
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-std::unique_ptr<VrFlinger> VrFlinger::Create(
- Hwc2::Composer* hidl, hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback) {
- std::unique_ptr<VrFlinger> vr_flinger(new VrFlinger);
- if (vr_flinger->Init(hidl, primary_display_id, request_display_callback))
- return vr_flinger;
- else
- return nullptr;
-}
-
-VrFlinger::VrFlinger() {}
-
-VrFlinger::~VrFlinger() {
- if (persistent_vr_state_callback_.get()) {
- sp<IVrManager> vr_manager = interface_cast<IVrManager>(
- defaultServiceManager()->checkService(String16("vrmanager")));
- if (vr_manager.get()) {
- vr_manager->unregisterPersistentVrStateListener(
- persistent_vr_state_callback_);
- }
- }
-
- if (dispatcher_)
- dispatcher_->SetCanceled(true);
- if (dispatcher_thread_.joinable())
- dispatcher_thread_.join();
-}
-
-bool VrFlinger::Init(Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback) {
- if (!hidl || !request_display_callback)
- return false;
-
- std::shared_ptr<android::pdx::Service> service;
-
- ALOGI("Starting up VrFlinger...");
-
- // We need to be able to create endpoints with full perms.
- umask(0000);
-
- android::ProcessState::self()->startThreadPool();
-
- request_display_callback_ = request_display_callback;
-
- dispatcher_ = android::pdx::ServiceDispatcher::Create();
- CHECK_ERROR(!dispatcher_, error, "Failed to create service dispatcher.");
-
- display_service_ = android::dvr::DisplayService::Create(
- hidl, primary_display_id, request_display_callback);
- CHECK_ERROR(!display_service_, error, "Failed to create display service.");
- dispatcher_->AddService(display_service_);
-
- service = android::dvr::DisplayManagerService::Create(display_service_);
- CHECK_ERROR(!service, error, "Failed to create display manager service.");
- dispatcher_->AddService(service);
-
- dispatcher_thread_ = std::thread([this]() {
- 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));
- }
- });
-
- return true;
-
-error:
- return false;
-}
-
-void VrFlinger::OnBootFinished() {
- display_service_->OnBootFinished();
- sp<IVrManager> vr_manager = interface_cast<IVrManager>(
- defaultServiceManager()->checkService(String16("vrmanager")));
- if (vr_manager.get()) {
- persistent_vr_state_callback_ =
- new PersistentVrStateCallback(request_display_callback_);
- vr_manager->registerPersistentVrStateListener(
- persistent_vr_state_callback_);
- } else {
- ALOGE("Unable to register vr flinger for persistent vr mode changes");
- }
-}
-
-void VrFlinger::GrantDisplayOwnership() {
- display_service_->GrantDisplayOwnership();
-}
-
-void VrFlinger::SeizeDisplayOwnership() {
- display_service_->SeizeDisplayOwnership();
-}
-
-std::string VrFlinger::Dump() {
- // TODO(karthikrs): Add more state information here.
- return display_service_->DumpState(0/*unused*/);
-}
-
-void VrFlinger::PersistentVrStateCallback::onPersistentVrStateChanged(
- bool enabled) {
- ALOGV("Notified persistent vr mode is %s", enabled ? "on" : "off");
- // TODO(eieio): Determine the correct signal to request display control.
- // Persistent VR mode is not enough.
- // request_display_callback_(enabled);
-}
-} // namespace dvr
-} // namespace android