diff options
Diffstat (limited to 'libs')
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 ⦥ } } @@ -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 Binary files differnew file mode 100644 index 0000000000..997b72d7a3 --- /dev/null +++ b/libs/renderengine/benchmark/resources/homescreen.png 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*>(×tamp)); - 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 |