From 278c5d851d979e824907e9d0ecf485449291e6e3 Mon Sep 17 00:00:00 2001 From: Vaibhav Devmurari Date: Thu, 19 Sep 2024 13:46:46 +0000 Subject: Add new policy flag to notify if a key gesture was triggred Policy needs to be passed a key event even if the key event was used in a gesture (to maintain state). So use this flag to inform policy that a key gesture was done using the KeyEvent Test: None Bug: 358569822 Flag: EXEMPT refactor Change-Id: I431377df09080ae42d467b610d234b0f7734e025 --- libs/input/android/os/IInputConstants.aidl | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'libs') diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index e23fc94c5e..31592cd6e3 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -48,6 +48,12 @@ interface IInputConstants */ const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000; + /** + * The key event triggered a key gesture. Used in policy flag to notify that a key gesture was + * triggered using the event. + */ + const int POLICY_FLAG_KEY_GESTURE_TRIGGERED = 0x40000; + /** * Common input event flag used for both motion and key events for a gesture or pointer being * canceled. -- cgit v1.2.3-59-g8ed1b From a40d560a56a8bbc626a77749bc7b24786df828b3 Mon Sep 17 00:00:00 2001 From: Marzia Favaro Date: Tue, 24 Sep 2024 12:57:37 +0000 Subject: Do not run edge extension benchmark if flag is off Otherwise we try running the shader without compiling it first and get a failure Test: librenderengine_bench Bug: 362241328 Flag: com.android.graphics.libgui.flags.edge_extension_shader Change-Id: I9f1b4c416776cc7b4ad865d6eb64201f46a390d8 --- libs/renderengine/benchmark/RenderEngineBench.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'libs') diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index a9264b3914..595573d24a 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -321,5 +322,7 @@ BENCHMARK_CAPTURE(BM_homescreen_blur, kawase_dual_filter, RenderEngine::Threaded BENCHMARK_CAPTURE(BM_homescreen, SkiaGLThreaded, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_EDGE_EXTENSION_SHADER BENCHMARK_CAPTURE(BM_homescreen_edgeExtension, SkiaGLThreaded, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL); +#endif -- cgit v1.2.3-59-g8ed1b From 187b974e7861fd6ec7f10288d9eb4beb2828a6bd Mon Sep 17 00:00:00 2001 From: Lais Andrade Date: Tue, 24 Sep 2024 17:08:59 +0000 Subject: Add libvibrator_test to presubmit Bug: 356144312 Change-Id: I19f72f9f5c69cde91079aac8e994c16f03e0532e Test: libvibrator_test Flag: EXEMPT test only --- libs/vibrator/TEST_MAPPING | 2 +- services/vibratorservice/TEST_MAPPING | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/vibrator/TEST_MAPPING b/libs/vibrator/TEST_MAPPING index d782b43b57..e206761efa 100644 --- a/libs/vibrator/TEST_MAPPING +++ b/libs/vibrator/TEST_MAPPING @@ -1,5 +1,5 @@ { - "postsubmit": [ + "presubmit": [ { "name": "libvibrator_test" } diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING index af486736d8..1eb3a581b7 100644 --- a/services/vibratorservice/TEST_MAPPING +++ b/services/vibratorservice/TEST_MAPPING @@ -4,11 +4,6 @@ "name": "libvibratorservice_test" } ], - "postsubmit": [ - { - "name": "libvibratorservice_test" - } - ], "imports": [ { "path": "cts/tests/vibrator" -- cgit v1.2.3-59-g8ed1b From e37f8346c62551dac1919fad4cd60a707d27e6a2 Mon Sep 17 00:00:00 2001 From: Paul Ramirez Date: Wed, 28 Aug 2024 18:42:21 +0000 Subject: Add multiple device resampling support to InputConsumerNoResampling with tests Added multiple device resampling support to InputConsumerNoResampling with unit tests to ensure correctness Bug: 297226446 Flag: EXEMPT refactor Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="InputConsumerTest*" Change-Id: I45528a89e0b60f46b0095078356382ed701b191b --- include/input/InputConsumerNoResampling.h | 25 +++-- include/input/Resampler.h | 6 -- libs/input/InputConsumerNoResampling.cpp | 65 +++++++----- libs/input/Resampler.cpp | 5 - libs/input/tests/InputConsumer_test.cpp | 160 ++++++++++++++++++++++++++++-- libs/input/tests/Resampler_test.cpp | 27 +---- libs/input/tests/TestEventMatchers.h | 99 ++++++++++++++++-- 7 files changed, 304 insertions(+), 83 deletions(-) (limited to 'libs') diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index c98b9cf8c1..228347d818 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -75,12 +76,13 @@ public: * the event is ready to consume. * @param looper needs to be sp and not shared_ptr because it inherits from * RefBase - * @param resampler the resampling strategy to use. If null, no resampling will be - * performed. + * @param resamplerCreator callable that returns the resampling strategy to be used. If null, no + * resampling will be performed. resamplerCreator must never return nullptr. */ - explicit InputConsumerNoResampling(const std::shared_ptr& channel, - sp looper, InputConsumerCallbacks& callbacks, - std::unique_ptr resampler); + explicit InputConsumerNoResampling( + const std::shared_ptr& channel, sp looper, + InputConsumerCallbacks& callbacks, + std::function()> resamplerCreator); ~InputConsumerNoResampling(); @@ -117,7 +119,13 @@ private: std::shared_ptr mChannel; sp mLooper; InputConsumerCallbacks& mCallbacks; - std::unique_ptr mResampler; + const std::function()> mResamplerCreator; + + /** + * A map to manage multidevice resampling. Each contained resampler is never null. This map is + * only modified by handleMessages. + */ + std::map> mResamplers; // Looper-related infrastructure /** @@ -190,7 +198,10 @@ private: /** * Batch messages that can be batched. When an unbatchable message is encountered, send it * to the InputConsumerCallbacks immediately. If there are batches remaining, - * notify InputConsumerCallbacks. + * notify InputConsumerCallbacks. If a resampleable ACTION_DOWN message is received, then a + * resampler is inserted for that deviceId in mResamplers. If a resampleable ACTION_UP or + * ACTION_CANCEL message is received then the resampler associated to that deviceId is erased + * from mResamplers. */ void handleMessages(std::vector&& messages); /** diff --git a/include/input/Resampler.h b/include/input/Resampler.h index dcb25b729f..4aaeddd159 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -91,12 +91,6 @@ private: } }; - /** - * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous - * and the current deviceId. - */ - std::optional mPreviousDeviceId; - /** * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called. * Note: We store up to two samples in order to simplify the implementation. Although, diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index cdbc1869c3..ce8bb43a76 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -17,8 +17,6 @@ #define LOG_TAG "InputConsumerNoResampling" #define ATRACE_TAG ATRACE_TAG_INPUT -#include - #include #include @@ -39,6 +37,8 @@ namespace { using std::chrono::nanoseconds; +using android::base::Result; + /** * Log debug messages relating to the consumer end of the transport channel. * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart) @@ -169,24 +169,18 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime; return msg; } - -bool isPointerEvent(const MotionEvent& motionEvent) { - return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; -} } // namespace -using android::base::Result; - // --- InputConsumerNoResampling --- -InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr& channel, - sp looper, - InputConsumerCallbacks& callbacks, - std::unique_ptr resampler) +InputConsumerNoResampling::InputConsumerNoResampling( + const std::shared_ptr& channel, sp looper, + InputConsumerCallbacks& callbacks, + std::function()> resamplerCreator) : mChannel{channel}, mLooper{looper}, mCallbacks{callbacks}, - mResampler{std::move(resampler)}, + mResamplerCreator{std::move(resamplerCreator)}, mFdEvents(0) { LOG_ALWAYS_FATAL_IF(mLooper == nullptr); mCallback = sp::make( @@ -319,7 +313,6 @@ void InputConsumerNoResampling::setFdEvents(int events) { } void InputConsumerNoResampling::handleMessages(std::vector&& messages) { - // TODO(b/297226446) : add resampling for (const InputMessage& msg : messages) { if (msg.header.type == InputMessage::Type::MOTION) { const int32_t action = msg.body.motion.action; @@ -329,12 +322,31 @@ void InputConsumerNoResampling::handleMessages(std::vector&& messa action == AMOTION_EVENT_ACTION_HOVER_MOVE) && (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER) || isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK)); + + const bool canResample = (mResamplerCreator != nullptr) && + (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)); + if (canResample) { + if (action == AMOTION_EVENT_ACTION_DOWN) { + if (std::unique_ptr resampler = mResamplerCreator(); + resampler != nullptr) { + const auto [_, inserted] = + mResamplers.insert(std::pair(deviceId, std::move(resampler))); + LOG_IF(WARNING, !inserted) << deviceId << "already exists in mResamplers"; + } + } + } + if (batchableEvent) { // add it to batch mBatches[deviceId].emplace(msg); } else { // consume all pending batches for this device immediately consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt); + if (canResample && + (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL)) { + LOG_IF(INFO, mResamplers.erase(deviceId) == 0) + << deviceId << "does not exist in mResamplers"; + } handleMessage(msg); } } else { @@ -456,8 +468,13 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrame std::queue& messages) { std::unique_ptr motionEvent; std::optional firstSeqForBatch; - const nanoseconds resampleLatency = - (mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0}; + + LOG_IF(FATAL, messages.empty()) << "messages queue is empty!"; + const DeviceId deviceId = messages.front().body.motion.deviceId; + const auto resampler = mResamplers.find(deviceId); + const nanoseconds resampleLatency = (resampler != mResamplers.cend()) + ? resampler->second->getResampleLatency() + : nanoseconds{0}; const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency; while (!messages.empty() && @@ -474,15 +491,17 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrame } messages.pop(); } + // Check if resampling should be performed. - if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) { - InputMessage* futureSample = nullptr; - if (!messages.empty()) { - futureSample = &messages.front(); - } - mResampler->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent, - futureSample); + InputMessage* futureSample = nullptr; + if (!messages.empty()) { + futureSample = &messages.front(); } + if ((motionEvent != nullptr) && (resampler != mResamplers.cend())) { + resampler->second->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent, + futureSample); + } + return std::make_pair(std::move(motionEvent), firstSeqForBatch); } diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp index 51fadf8ec1..328fa684f6 100644 --- a/libs/input/Resampler.cpp +++ b/libs/input/Resampler.cpp @@ -247,11 +247,6 @@ nanoseconds LegacyResampler::getResampleLatency() const { void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent, const InputMessage* futureSample) { - if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) { - mLatestSamples.clear(); - } - mPreviousDeviceId = motionEvent.getDeviceId(); - const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY; updateLatestSamples(motionEvent); diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp index d708316236..cbb332ed7f 100644 --- a/libs/input/tests/InputConsumer_test.cpp +++ b/libs/input/tests/InputConsumer_test.cpp @@ -16,6 +16,9 @@ #include +#include + +#include #include #include @@ -25,7 +28,9 @@ #include #include #include +#include #include +#include #include #include @@ -37,8 +42,18 @@ using std::chrono::nanoseconds; using ::testing::AllOf; using ::testing::Matcher; -using ::testing::Not; +struct Pointer { + int32_t id{0}; + ToolType toolType{ToolType::FINGER}; + float x{0.0f}; + float y{0.0f}; + bool isResampled{false}; + + PointerBuilder asPointerBuilder() const { + return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled); + } +}; } // namespace class InputConsumerTest : public testing::Test, public InputConsumerCallbacks { @@ -47,9 +62,9 @@ protected: : mClientTestChannel{std::make_shared("TestChannel")}, mLooper{sp::make(/*allowNonCallbacks=*/false)} { Looper::setForThread(mLooper); - mConsumer = - std::make_unique(mClientTestChannel, mLooper, *this, - std::make_unique()); + mConsumer = std::make_unique< + InputConsumerNoResampling>(mClientTestChannel, mLooper, *this, + []() { return std::make_unique(); }); } void invokeLooperCallback() const { @@ -71,6 +86,9 @@ protected: EXPECT_THAT(*motionEvent, matcher); } + InputMessage nextPointerMessage(std::chrono::nanoseconds eventTime, DeviceId deviceId, + int32_t action, const Pointer& pointer); + std::shared_ptr mClientTestChannel; sp mLooper; std::unique_ptr mConsumer; @@ -83,6 +101,7 @@ protected: BlockingQueue> mTouchModeEvents; private: + uint32_t mLastSeq{0}; size_t mOnBatchedInputEventPendingInvocationCount{0}; // InputConsumerCallbacks interface @@ -118,6 +137,19 @@ private: }; }; +InputMessage InputConsumerTest::nextPointerMessage(std::chrono::nanoseconds eventTime, + DeviceId deviceId, int32_t action, + const Pointer& pointer) { + ++mLastSeq; + return InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq} + .eventTime(eventTime.count()) + .deviceId(deviceId) + .source(AINPUT_SOURCE_TOUCHSCREEN) + .action(action) + .pointer(pointer.asPointerBuilder()) + .build(); +} + TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) { mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} .eventTime(nanoseconds{0ms}.count()) @@ -235,8 +267,7 @@ TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) { .build()); invokeLooperCallback(); - assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - Not(MotionEventIsResampled()))); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true); @@ -244,4 +275,121 @@ TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) { mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true); } + +/** + * The test supposes a 60Hz Vsync rate and a 200Hz input rate. The InputMessages are intertwined as + * in a real use cases. The test's two devices should be resampled independently. Moreover, the + * InputMessage stream layout for the test is: + * + * DOWN(0, 0ms) + * MOVE(0, 5ms) + * MOVE(0, 10ms) + * DOWN(1, 15ms) + * + * CONSUME(16ms) + * + * MOVE(1, 20ms) + * MOVE(1, 25ms) + * MOVE(0, 30ms) + * + * CONSUME(32ms) + * + * MOVE(0, 35ms) + * UP(1, 40ms) + * UP(0, 45ms) + * + * CONSUME(48ms) + * + * The first field is device ID, and the second field is event time. + */ +TEST_F(InputConsumerTest, MultiDeviceResampling) { + mClientTestChannel->enqueueMessage(nextPointerMessage(0ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_DOWN, + Pointer{.x = 0, .y = 0})); + + mClientTestChannel->assertNoSentMessages(); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSampleCount(1))); + + mClientTestChannel->enqueueMessage(nextPointerMessage(5ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 1.0f, .y = 2.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(10ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 2.0f, .y = 4.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(15ms, /*deviceId=*/1, + AMOTION_EVENT_ACTION_DOWN, + Pointer{.x = 10.0f, .y = 10.0f})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/); + + assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSampleCount(1))); + assertReceivedMotionEvent( + AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3), + WithSample(/*sampleIndex=*/2, + Sample{11ms, + {PointerArgs{.x = 2.2f, .y = 4.4f, .isResampled = true}}}))); + + mClientTestChannel->enqueueMessage(nextPointerMessage(20ms, /*deviceId=*/1, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 11.0f, .y = 12.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(25ms, /*deviceId=*/1, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 12.0f, .y = 14.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(30ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 5.0f, .y = 6.0f})); + + invokeLooperCallback(); + assertOnBatchedInputEventPendingWasCalled(); + mConsumer->consumeBatchedInputEvents(32'000'000 /*ns*/); + + assertReceivedMotionEvent( + AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3), + WithSample(/*sampleIndex=*/2, + Sample{27ms, + {PointerArgs{.x = 12.4f, .y = 14.8f, .isResampled = true}}}))); + + mClientTestChannel->enqueueMessage(nextPointerMessage(35ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 8.0f, .y = 9.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(40ms, /*deviceId=*/1, + AMOTION_EVENT_ACTION_UP, + Pointer{.x = 12.0f, .y = 14.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(45ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_UP, + Pointer{.x = 8.0f, .y = 9.0f})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(48'000'000 /*ns*/); + + assertReceivedMotionEvent( + AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSampleCount(1))); + + assertReceivedMotionEvent( + AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3), + WithSample(/*sampleIndex=*/2, + Sample{37'500'000ns, + {PointerArgs{.x = 9.5f, .y = 10.5f, .isResampled = true}}}))); + + assertReceivedMotionEvent( + AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSampleCount(1))); + + // The sequence order is based on the expected consumption. Each sequence number corresponds to + // one of the previously enqueued messages. + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/5, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/6, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/9, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/7, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/8, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/10, /*handled=*/true); +} } // namespace android diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp index 26dee393c1..fae8518e87 100644 --- a/libs/input/tests/Resampler_test.cpp +++ b/libs/input/tests/Resampler_test.cpp @@ -87,7 +87,6 @@ InputSample::operator InputMessage() const { struct InputStream { std::vector samples{}; int32_t action{0}; - DeviceId deviceId{0}; /** * Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with * the relevant data for tests. @@ -100,8 +99,8 @@ InputStream::operator MotionEvent() const { MotionEventBuilder motionEventBuilder = MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER) .downTime(0) - .eventTime(static_cast(firstSample.eventTime).count()) - .deviceId(deviceId); + .eventTime( + static_cast(firstSample.eventTime).count()); for (const Pointer& pointer : firstSample.pointers) { const PointerBuilder pointerBuilder = PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y); @@ -289,28 +288,6 @@ TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) { assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } -TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) { - MotionEvent motionFromFirstDevice = - InputStream{{InputSample{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, - InputSample{8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, - AMOTION_EVENT_ACTION_MOVE, - .deviceId = 0}; - - mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr); - - MotionEvent motionFromSecondDevice = - InputStream{{InputSample{11ms, - {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, - AMOTION_EVENT_ACTION_MOVE, - .deviceId = 1}; - const MotionEvent originalMotionEvent = motionFromSecondDevice; - - mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr); - // The MotionEvent should not be resampled because the second event came from a different device - // than the previous event. - assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice); -} - TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) { MotionEvent motionEvent = InputStream{{InputSample{10ms, diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h index dd2e40c025..3589de599f 100644 --- a/libs/input/tests/TestEventMatchers.h +++ b/libs/input/tests/TestEventMatchers.h @@ -16,18 +16,39 @@ #pragma once +#include #include +#include +#include +#include #include namespace android { +namespace { + +using ::testing::Matcher; + +} // namespace + /** * This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally, * implementations must not be duplicated. * TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput. */ +struct PointerArgs { + float x{0.0f}; + float y{0.0f}; + bool isResampled{false}; +}; + +struct Sample { + std::chrono::nanoseconds eventTime{0}; + std::vector pointers{}; +}; + class WithDeviceIdMatcher { public: using is_gtest_matcher = void; @@ -79,32 +100,88 @@ inline WithMotionActionMatcher WithMotionAction(int32_t action) { return WithMotionActionMatcher(action); } -class MotionEventIsResampledMatcher { +class WithSampleCountMatcher { public: using is_gtest_matcher = void; + explicit WithSampleCountMatcher(size_t sampleCount) : mExpectedSampleCount{sampleCount} {} bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const { - const size_t numSamples = motionEvent.getHistorySize() + 1; - const size_t numPointers = motionEvent.getPointerCount(); - if (numPointers <= 0 || numSamples <= 0) { + return (motionEvent.getHistorySize() + 1) == mExpectedSampleCount; + } + + void DescribeTo(std::ostream* os) const { *os << "sample count " << mExpectedSampleCount; } + + void DescribeNegationTo(std::ostream* os) const { *os << "different sample count"; } + +private: + const size_t mExpectedSampleCount; +}; + +inline WithSampleCountMatcher WithSampleCount(size_t sampleCount) { + return WithSampleCountMatcher(sampleCount); +} + +class WithSampleMatcher { +public: + using is_gtest_matcher = void; + explicit WithSampleMatcher(size_t sampleIndex, const Sample& sample) + : mSampleIndex{sampleIndex}, mSample{sample} {} + + bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream* os) const { + if (motionEvent.getHistorySize() < mSampleIndex) { + *os << "sample index out of bounds"; + return false; + } + + if (motionEvent.getHistoricalEventTime(mSampleIndex) != mSample.eventTime.count()) { + *os << "event time mismatch. sample: " + << motionEvent.getHistoricalEventTime(mSampleIndex) + << " expected: " << mSample.eventTime.count(); + return false; + } + + if (motionEvent.getPointerCount() != mSample.pointers.size()) { + *os << "pointer count mismatch. sample: " << motionEvent.getPointerCount() + << " expected: " << mSample.pointers.size(); return false; } - for (size_t i = 0; i < numPointers; ++i) { + + for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); + ++pointerIndex) { const PointerCoords& pointerCoords = - motionEvent.getSamplePointerCoords()[numSamples * numPointers + i]; - if (!pointerCoords.isResampled) { + *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex)); + if ((pointerCoords.getX() != mSample.pointers[pointerIndex].x) || + (pointerCoords.getY() != mSample.pointers[pointerIndex].y)) { + *os << "sample coordinates mismatch at pointer index " << pointerIndex + << ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY() + << ") expected: (" << mSample.pointers[pointerIndex].x << ", " + << mSample.pointers[pointerIndex].y << ")"; + return false; + } + if (motionEvent.isResampled(pointerIndex, mSampleIndex) != + mSample.pointers[pointerIndex].isResampled) { + *os << "resampling flag mismatch. sample: " + << motionEvent.isResampled(pointerIndex, mSampleIndex) + << " expected: " << mSample.pointers[pointerIndex].isResampled; return false; } } return true; } - void DescribeTo(std::ostream* os) const { *os << "MotionEvent is resampled."; } + void DescribeTo(std::ostream* os) const { *os << "motion event sample properties match."; } - void DescribeNegationTo(std::ostream* os) const { *os << "MotionEvent is not resampled."; } + void DescribeNegationTo(std::ostream* os) const { + *os << "motion event sample properties do not match expected properties."; + } + +private: + const size_t mSampleIndex; + const Sample mSample; }; -inline MotionEventIsResampledMatcher MotionEventIsResampled() { - return MotionEventIsResampledMatcher(); +inline WithSampleMatcher WithSample(size_t sampleIndex, const Sample& sample) { + return WithSampleMatcher(sampleIndex, sample); } + } // namespace android -- cgit v1.2.3-59-g8ed1b From 58bda65dfedae9be281a3399fc082c4ee9265fee Mon Sep 17 00:00:00 2001 From: Yeabkal Wubshit Date: Tue, 24 Sep 2024 20:22:18 -0700 Subject: Rotary encoder rotation count telemetry Implements a telemetry using the Telemetry Express API to log full rotations on rotary encoder devices. By default, logs are disabled for rotations. A rotary input device can change the minimum logged rotation value via the `rotary_encoder.min_rotations_to_log` IDC property, by setting it to a positive integer value. Bug: 370353565 Test: atest RotaryEncoderInputMapperTest Test: manual with custom logs Flag: com.android.input.flags.rotary_input_telemetry Change-Id: I5162b0d343936ac8049c24835cd8e57d44643516 --- libs/input/input_flags.aconfig | 7 + services/inputflinger/reader/Android.bp | 4 + .../reader/mapper/RotaryEncoderInputMapper.cpp | 53 +++++++- .../reader/mapper/RotaryEncoderInputMapper.h | 26 ++++ .../tests/RotaryEncoderInputMapper_test.cpp | 149 +++++++++++++++++++++ 5 files changed, 237 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 60fb00e128..701fb43c1f 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -207,3 +207,10 @@ flag { description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates." bug: "336585002" } + +flag { + name: "rotary_input_telemetry" + namespace: "wear_frameworks" + description: "Enable telemetry for rotary input" + bug: "370353565" +} diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index b76e8c515f..b3cd35c936 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -90,10 +90,14 @@ cc_defaults { "libstatslog", "libstatspull", "libutils", + "libstatssocket", ], static_libs: [ "libchrome-gestures", "libui-types", + "libexpresslog", + "libtextclassifier_hash_static", + "libstatslog_express", ], header_libs: [ "libbatteryservice_headers", diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index b72cc6e060..c633b495e4 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -20,6 +20,8 @@ #include "RotaryEncoderInputMapper.h" +#include +#include #include #include @@ -27,14 +29,26 @@ namespace android { +using android::expresslog::Counter; + +constexpr float kDefaultResolution = 0; constexpr float kDefaultScaleFactor = 1.0f; +constexpr int32_t kDefaultMinRotationsToLog = 3; RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig) + : RotaryEncoderInputMapper(deviceContext, readerConfig, + Counter::logIncrement /* telemetryLogCounter */) {} + +RotaryEncoderInputMapper::RotaryEncoderInputMapper( + InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig, + std::function telemetryLogCounter) : InputMapper(deviceContext, readerConfig), mSource(AINPUT_SOURCE_ROTARY_ENCODER), mScalingFactor(kDefaultScaleFactor), - mOrientation(ui::ROTATION_0) {} + mResolution(kDefaultResolution), + mOrientation(ui::ROTATION_0), + mTelemetryLogCounter(telemetryLogCounter) {} RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} @@ -51,6 +65,7 @@ void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo& info) { if (!res.has_value()) { ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); } + mResolution = res.value_or(kDefaultResolution); std::optional scalingFactor = config.getFloat("device.scalingFactor"); if (!scalingFactor.has_value()) { ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," @@ -59,7 +74,22 @@ void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo& info) { } mScalingFactor = scalingFactor.value_or(kDefaultScaleFactor); info.addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - res.value_or(0.0f) * mScalingFactor); + mResolution * mScalingFactor); + + if (com::android::input::flags::rotary_input_telemetry()) { + mMinRotationsToLog = config.getInt("rotary_encoder.min_rotations_to_log"); + if (!mMinRotationsToLog.has_value()) { + ALOGI("Rotary Encoder device configuration file didn't specify min log rotation."); + } else if (*mMinRotationsToLog <= 0) { + ALOGE("Rotary Encoder device configuration specified non-positive min log rotation " + ": %d. Telemetry logging of rotations disabled.", + *mMinRotationsToLog); + mMinRotationsToLog = {}; + } else { + ALOGD("Rotary Encoder telemetry enabled. mMinRotationsToLog=%d", + *mMinRotationsToLog); + } + } } } @@ -121,10 +151,29 @@ std::list RotaryEncoderInputMapper::process(const RawEvent& rawEvent return out; } +void RotaryEncoderInputMapper::logScroll(float scroll) { + if (mResolution <= 0 || !mMinRotationsToLog) return; + + mUnloggedScrolls += fabs(scroll); + + // unitsPerRotation = (2 * PI * radians) * (units per radian (i.e. resolution)) + const float unitsPerRotation = 2 * M_PI * mResolution; + const float scrollsPerMinRotationsToLog = *mMinRotationsToLog * unitsPerRotation; + const int32_t numMinRotationsToLog = + static_cast(mUnloggedScrolls / scrollsPerMinRotationsToLog); + mUnloggedScrolls = std::fmod(mUnloggedScrolls, scrollsPerMinRotationsToLog); + if (numMinRotationsToLog) { + mTelemetryLogCounter("input.value_rotary_input_device_full_rotation_count", + numMinRotationsToLog * (*mMinRotationsToLog)); + } +} + std::list RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) { std::list out; float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); + logScroll(scroll); + if (mSlopController) { scroll = mSlopController->consumeEvent(when, scroll); } diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index 7e804150b1..d74ced180e 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -46,13 +46,39 @@ private: int32_t mSource; float mScalingFactor; + /** Units per rotation, provided via the `device.res` IDC property. */ + float mResolution; ui::Rotation mOrientation; + /** + * The minimum number of rotations to log for telemetry. + * Provided via `rotary_encoder.min_rotations_to_log` IDC property. If no value is provided in + * the IDC file, or if a non-positive value is provided, the telemetry will be disabled, and + * this value is set to the empty optional. + */ + std::optional mMinRotationsToLog; + /** + * A function to log count for telemetry. + * The char* is the logging key, and the int64_t is the value to log. + * Abstracting the actual logging APIs via this function is helpful for simple unit testing. + */ + std::function mTelemetryLogCounter; ui::LogicalDisplayId mDisplayId = ui::LogicalDisplayId::INVALID; std::unique_ptr mSlopController; + /** Amount of raw scrolls (pre-slop) not yet logged for telemetry. */ + float mUnloggedScrolls = 0; + explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig); + + /** This is a test constructor that allows injecting the expresslog Counter logic. */ + RotaryEncoderInputMapper(InputDeviceContext& deviceContext, + const InputReaderConfiguration& readerConfig, + std::function expressLogCounter); [[nodiscard]] std::list sync(nsecs_t when, nsecs_t readTime); + + /** Logs a given amount of scroll for telemetry. */ + void logScroll(float scroll); }; } // namespace android diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp index 6607bc7972..486d893944 100644 --- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp +++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include #include @@ -100,6 +102,15 @@ protected: EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) .WillRepeatedly(Return(false)); } + + std::map mTelemetryLogCounts; + + /** + * A fake function for telemetry logging. + * Records the log counts in the `mTelemetryLogCounts` map. + */ + std::function mTelemetryLogCounter = + [this](const char* key, int64_t value) { mTelemetryLogCounts[key] += value; }; }; TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdWithAssociatedViewport) { @@ -187,4 +198,142 @@ TEST_F(RotaryEncoderInputMapperTest, HighResScrollIgnoresRegularScroll) { WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f))))); } +TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, RotaryInputTelemetryFlagOff_NoRotationLogging, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags, + rotary_input_telemetry))) { + mPropertyMap.addProperty("device.res", "3"); + mMapper = createInputMapper(*mDeviceContext, mReaderConfiguration, + mTelemetryLogCounter); + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + std::list args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 70); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"), + mTelemetryLogCounts.end()); +} + +TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, ZeroResolution_NoRotationLogging, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rotary_input_telemetry))) { + mPropertyMap.addProperty("device.res", "-3"); + mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "2"); + mMapper = createInputMapper(*mDeviceContext, mReaderConfiguration, + mTelemetryLogCounter); + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + std::list args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"), + mTelemetryLogCounts.end()); +} + +TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, NegativeMinLogRotation_NoRotationLogging, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rotary_input_telemetry))) { + mPropertyMap.addProperty("device.res", "3"); + mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "-2"); + mMapper = createInputMapper(*mDeviceContext, mReaderConfiguration, + mTelemetryLogCounter); + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + std::list args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"), + mTelemetryLogCounts.end()); +} + +TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, ZeroMinLogRotation_NoRotationLogging, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rotary_input_telemetry))) { + mPropertyMap.addProperty("device.res", "3"); + mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "0"); + mMapper = createInputMapper(*mDeviceContext, mReaderConfiguration, + mTelemetryLogCounter); + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + std::list args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"), + mTelemetryLogCounts.end()); +} + +TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, NoMinLogRotation_NoRotationLogging, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rotary_input_telemetry))) { + // 3 units per radian, 2 * M_PI * 3 = ~18.85 units per rotation. + mPropertyMap.addProperty("device.res", "3"); + mMapper = createInputMapper(*mDeviceContext, mReaderConfiguration, + mTelemetryLogCounter); + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + std::list args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"), + mTelemetryLogCounts.end()); +} + +TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, RotationLogging, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rotary_input_telemetry))) { + // 3 units per radian, 2 * M_PI * 3 = ~18.85 units per rotation. + // Multiples of `unitsPerRoation`, to easily follow the assertions below. + // [18.85, 37.7, 56.55, 75.4, 94.25, 113.1, 131.95, 150.8] + mPropertyMap.addProperty("device.res", "3"); + mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "2"); + + mMapper = createInputMapper(*mDeviceContext, mReaderConfiguration, + mTelemetryLogCounter); + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + std::list args; + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 15); // total scroll = 15 + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"), + mTelemetryLogCounts.end()); + + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 13); // total scroll = 28 + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + // Expect 0 since `min_rotations_to_log` = 2, and total scroll 28 only has 1 rotation. + ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"), + mTelemetryLogCounts.end()); + + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 10); // total scroll = 38 + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + // Total scroll includes >= `min_rotations_to_log` (2), expect log. + ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 2); + + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -22); // total scroll = 60 + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + // Expect no additional telemetry. Total rotation is 3, and total unlogged rotation is 1, which + // is less than `min_rotations_to_log`. + ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 2); + + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -16); // total scroll = 76 + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + // Total unlogged rotation >= `min_rotations_to_log` (2), so expect 2 more logged rotation. + ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 4); + + args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -76); // total scroll = 152 + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + // Total unlogged scroll >= 4*`min_rotations_to_log`. Expect *all* unlogged rotations to be + // logged, even if that's more than multiple of `min_rotations_to_log`. + ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 8); +} + } // namespace android \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 72963bc3191455232650e027cf0d90d868fdf36d Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 25 Sep 2024 23:36:04 +0000 Subject: binder_parcel_fuzzer: support write functions This generalizes the append parcel fuzzing functionality, for additional hardening. Fixes: 328161314 Test: manual, run binder_parcel_fuzzer for 3min Ignore-AOSP-First: internal only for security hardening Change-Id: I1f470e0d56241327bff7422d061912b592cc0ddc --- libs/binder/tests/parcel_fuzzer/binder.cpp | 120 ++++++++++++++++++++++++ libs/binder/tests/parcel_fuzzer/binder.h | 1 + libs/binder/tests/parcel_fuzzer/binder_ndk.cpp | 54 +++++++++-- libs/binder/tests/parcel_fuzzer/binder_ndk.h | 1 + libs/binder/tests/parcel_fuzzer/main.cpp | 46 +++++---- libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h | 4 + 6 files changed, 200 insertions(+), 26 deletions(-) (limited to 'libs') diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index e378b864f7..a9c1fed756 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include "../../Utils.h" @@ -404,5 +406,123 @@ std::vector> BINDER_PARCEL_READ_FUNCTIONS { FUZZ_LOG() << " toString() result: " << toString; }, }; + +std::vector> BINDER_PARCEL_WRITE_FUNCTIONS { + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call setDataSize"; + size_t len = provider.ConsumeIntegralInRange(0, 1024); + p.setDataSize(len); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call setDataCapacity"; + size_t len = provider.ConsumeIntegralInRange(0, 1024); + p.setDataCapacity(len); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call setData"; + size_t len = provider.ConsumeIntegralInRange(0, 1024); + std::vector bytes = provider.ConsumeBytes(len); + p.setData(bytes.data(), bytes.size()); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) { + FUZZ_LOG() << "about to call appendFrom"; + + std::vector bytes = provider.ConsumeBytes(provider.ConsumeIntegralInRange(0, 4096)); + ::android::Parcel p2; + fillRandomParcel(&p2, FuzzedDataProvider(bytes.data(), bytes.size()), options); + + int32_t start = provider.ConsumeIntegral(); + int32_t len = provider.ConsumeIntegral(); + p.appendFrom(&p2, start, len); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call setData"; + size_t len = provider.ConsumeIntegralInRange(0, 1024); + std::vector bytes = provider.ConsumeBytes(len); + p.setData(bytes.data(), bytes.size()); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call pushAllowFds"; + bool val = provider.ConsumeBool(); + p.pushAllowFds(val); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call restoreAllowFds"; + bool val = provider.ConsumeBool(); + p.restoreAllowFds(val); + }, + // markForBinder - covered by fillRandomParcel, aborts if called multiple times + // markForRpc - covered by fillRandomParcel, aborts if called multiple times + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call writeInterfaceToken"; + std::string interface = provider.ConsumeRandomLengthString(); + p.writeInterfaceToken(android::String16(interface.c_str())); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call setEnforceNoDataAvail"; + p.setEnforceNoDataAvail(provider.ConsumeBool()); + }, + [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call setServiceFuzzing"; + p.setServiceFuzzing(); + }, + [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call freeData"; + p.freeData(); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call write"; + size_t len = provider.ConsumeIntegralInRange(0, 256); + std::vector bytes = provider.ConsumeBytes(len); + p.write(bytes.data(), bytes.size()); + }, + // write* - write functions all implemented by calling 'write' itself. + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) { + FUZZ_LOG() << "about to call writeStrongBinder"; + + // TODO: this logic is somewhat duplicated with random parcel + android::sp binder; + if (provider.ConsumeBool() && options->extraBinders.size() > 0) { + binder = options->extraBinders.at( + provider.ConsumeIntegralInRange(0, options->extraBinders.size() - 1)); + } else { + binder = android::getRandomBinder(&provider); + options->extraBinders.push_back(binder); + } + + p.writeStrongBinder(binder); + }, + [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call writeFileDescriptor (no ownership)"; + p.writeFileDescriptor(STDERR_FILENO, false /* takeOwnership */); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) { + FUZZ_LOG() << "about to call writeFileDescriptor (take ownership)"; + std::vector fds = android::getRandomFds(&provider); + if (fds.size() == 0) return; + + p.writeDupFileDescriptor(fds.at(0).get()); + options->extraFds.insert(options->extraFds.end(), + std::make_move_iterator(fds.begin() + 1), + std::make_move_iterator(fds.end())); + }, + // TODO: writeBlob + // TODO: writeDupImmutableBlobFileDescriptor + // TODO: writeObject (or make the API private more likely) + [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call writeNoException"; + p.writeNoException(); + }, + [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call closeFileDescriptors"; + p.closeFileDescriptors(); + }, + [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call replaceCallingWorkSourceUid"; + uid_t uid = provider.ConsumeIntegral(); + p.replaceCallingWorkSourceUid(uid); + }, +}; + // clang-format on #pragma clang diagnostic pop diff --git a/libs/binder/tests/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h index 0c51d68a37..b0ac140d3f 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.h +++ b/libs/binder/tests/parcel_fuzzer/binder.h @@ -21,3 +21,4 @@ #include "parcel_fuzzer.h" extern std::vector> BINDER_PARCEL_READ_FUNCTIONS; +extern std::vector> BINDER_PARCEL_WRITE_FUNCTIONS; diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index 3a1471eabe..d17fc9601c 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -20,8 +20,11 @@ #include "aidl/parcelables/GenericDataParcelable.h" #include "aidl/parcelables/SingleDataParcelable.h" +#include #include #include +#include +#include #include "util.h" @@ -210,16 +213,51 @@ std::vector> BINDER_NDK_PARCEL_READ_FUNCTIONS{ binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len); FUZZ_LOG() << "status: " << status; }, - [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) { - FUZZ_LOG() << "about to unmarshal AParcel"; +}; +std::vector> BINDER_NDK_PARCEL_WRITE_FUNCTIONS{ + [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) { + FUZZ_LOG() << "about to call AParcel_writeStrongBinder"; + + // TODO: this logic is somewhat duplicated with random parcel + android::sp binder; + if (provider.ConsumeBool() && options->extraBinders.size() > 0) { + binder = options->extraBinders.at( + provider.ConsumeIntegralInRange(0, options->extraBinders.size() - 1)); + } else { + binder = android::getRandomBinder(&provider); + options->extraBinders.push_back(binder); + } + + ndk::SpAIBinder abinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(binder)); + AParcel_writeStrongBinder(p.aParcel(), abinder.get()); + }, + [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) { + FUZZ_LOG() << "about to call AParcel_writeParcelFileDescriptor"; + + auto fds = android::getRandomFds(&provider); + if (fds.size() == 0) return; + + AParcel_writeParcelFileDescriptor(p.aParcel(), fds.at(0).get()); + options->extraFds.insert(options->extraFds.end(), + std::make_move_iterator(fds.begin() + 1), + std::make_move_iterator(fds.end())); + }, + // all possible data writes can be done as a series of 4-byte reads + [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call AParcel_writeInt32"; + int32_t val = provider.ConsumeIntegral(); + AParcel_writeInt32(p.aParcel(), val); + }, + [] (NdkParcelAdapter& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call AParcel_reset"; + AParcel_reset(p.aParcel()); + }, + [](NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { + FUZZ_LOG() << "about to call AParcel_unmarshal"; size_t len = provider.ConsumeIntegralInRange(0, provider.remaining_bytes()); - std::vector parcelData = provider.ConsumeBytes(len); - const uint8_t* buffer = parcelData.data(); - const size_t bufferLen = parcelData.size(); - NdkParcelAdapter adapter; - binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen); + std::vector data = provider.ConsumeBytes(len); + binder_status_t status = AParcel_unmarshal(p.aParcel(), data.data(), data.size()); FUZZ_LOG() << "status: " << status; }, - }; // clang-format on diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h index d19f25bc88..0c8b725400 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h @@ -50,3 +50,4 @@ private: }; extern std::vector> BINDER_NDK_PARCEL_READ_FUNCTIONS; +extern std::vector> BINDER_NDK_PARCEL_WRITE_FUNCTIONS; diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index a57d07fb24..ede0e925ee 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -80,6 +80,7 @@ void doTransactFuzz(const char* backend, const sp& binder, FuzzedDataProvider (void)binder->transact(code, data, &reply, flag); } +// start with a Parcel full of data (e.g. like you get from another process) template void doReadFuzz(const char* backend, const std::vector>& reads, FuzzedDataProvider&& provider) { @@ -98,7 +99,7 @@ void doReadFuzz(const char* backend, const std::vector>& reads, fillRandomParcel(&p, std::move(provider), &options); // since we are only using a byte to index - CHECK(reads.size() <= 255) << reads.size(); + CHECK_LE(reads.size(), 255u) << reads.size(); FUZZ_LOG() << "backend: " << backend; FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize()); @@ -115,26 +116,31 @@ void doReadFuzz(const char* backend, const std::vector>& reads, } } -// Append two random parcels. template -void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) { - int32_t start = provider.ConsumeIntegral(); - int32_t len = provider.ConsumeIntegral(); - - std::vector bytes = provider.ConsumeBytes( - provider.ConsumeIntegralInRange(0, provider.remaining_bytes())); - - // same options so that FDs and binders could be shared in both Parcels +void doReadWriteFuzz(const char* backend, const std::vector>& reads, + const std::vector>& writes, FuzzedDataProvider&& provider) { RandomParcelOptions options; - P p0, p1; - fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options); - fillRandomParcel(&p1, std::move(provider), &options); + P p; + fillRandomParcel(&p, std::move(provider), &options); + + // since we are only using a byte to index + CHECK_LE(reads.size() + writes.size(), 255u) << reads.size(); FUZZ_LOG() << "backend: " << backend; - FUZZ_LOG() << "start: " << start << " len: " << len; - p0.appendFrom(&p1, start, len); + while (provider.remaining_bytes() > 0) { + uint8_t idx = provider.ConsumeIntegralInRange(0, reads.size() + writes.size() - 1); + + FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail() + << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity(); + + if (idx < reads.size()) { + reads.at(idx)(p, provider); + } else { + writes.at(idx - reads.size())(p, provider, &options); + } + } } void* NothingClass_onCreate(void* args) { @@ -156,7 +162,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { FuzzedDataProvider provider = FuzzedDataProvider(data, size); - const std::function fuzzBackend[] = { + const std::function fuzzBackend[] = { [](FuzzedDataProvider&& provider) { doTransactFuzz< ::android::hardware::Parcel>("hwbinder", @@ -187,10 +193,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { std::move(provider)); }, [](FuzzedDataProvider&& provider) { - doAppendFuzz<::android::Parcel>("binder", std::move(provider)); + doReadWriteFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS, + BINDER_PARCEL_WRITE_FUNCTIONS, + std::move(provider)); }, [](FuzzedDataProvider&& provider) { - doAppendFuzz("binder_ndk", std::move(provider)); + doReadWriteFuzz("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS, + BINDER_NDK_PARCEL_WRITE_FUNCTIONS, + std::move(provider)); }, }; diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h index 765a93e8c9..dbd0caea01 100644 --- a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h +++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h @@ -15,9 +15,13 @@ */ #pragma once +#include #include #include template using ParcelRead = std::function; +template +using ParcelWrite = std::function; -- cgit v1.2.3-59-g8ed1b From 19ff63eea17a7dbdc1e79e64308fadf7d03ca464 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Tue, 1 Oct 2024 18:27:14 +0000 Subject: binder_parcel_fuzzer: cleanup dups/to remove - setData was represented multiple times - closeFileDescriptors should be private Ignore-AOSP-First: fuzzer work Bug: 328161314 Test: N/A Change-Id: I3c5d4c96c69788bc71a01ad3af971c99b162a970 --- libs/binder/tests/parcel_fuzzer/binder.cpp | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'libs') diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index a9c1fed756..07f0143767 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -117,14 +117,6 @@ std::vector> BINDER_PARCEL_READ_FUNCTIONS { p.setDataPosition(pos); FUZZ_LOG() << "setDataPosition done"; }, - [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { - size_t len = provider.ConsumeIntegralInRange(0, 1024); - std::vector bytes = provider.ConsumeBytes(len); - FUZZ_LOG() << "about to setData: " <<(bytes.data() ? HexString(bytes.data(), bytes.size()) : "null"); - // TODO: allow all read and write operations - (*const_cast<::android::Parcel*>(&p)).setData(bytes.data(), bytes.size()); - FUZZ_LOG() << "setData done"; - }, PARCEL_READ_NO_STATUS(size_t, allowFds), PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), PARCEL_READ_NO_STATUS(std::vector>, debugReadAllStrongBinders), @@ -435,12 +427,6 @@ std::vector> BINDER_PARCEL_WRITE_FUNCTIONS { int32_t len = provider.ConsumeIntegral(); p.appendFrom(&p2, start, len); }, - [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { - FUZZ_LOG() << "about to call setData"; - size_t len = provider.ConsumeIntegralInRange(0, 1024); - std::vector bytes = provider.ConsumeBytes(len); - p.setData(bytes.data(), bytes.size()); - }, [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { FUZZ_LOG() << "about to call pushAllowFds"; bool val = provider.ConsumeBool(); @@ -513,10 +499,6 @@ std::vector> BINDER_PARCEL_WRITE_FUNCTIONS { FUZZ_LOG() << "about to call writeNoException"; p.writeNoException(); }, - [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) { - FUZZ_LOG() << "about to call closeFileDescriptors"; - p.closeFileDescriptors(); - }, [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) { FUZZ_LOG() << "about to call replaceCallingWorkSourceUid"; uid_t uid = provider.ConsumeIntegral(); -- cgit v1.2.3-59-g8ed1b From 1021015c7dce890a078e0e9f21db73f000fdc878 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Tue, 1 Oct 2024 19:15:16 +0000 Subject: binder_parcel_fuzzer: avoid consuming all provider In doReadWriteFuzz, it should not call fillRandomParcel, as this consumes the entire provider. It's meant to always start with an empty Parcel, but I forgot to remove this. Ignore-AOSP-First: fuzzer work Bug: 328161314 Test: run fuzzer, starts finding crashes Change-Id: I1ce474a4e39464fd53f6cd9c440b40bd128fada1 --- libs/binder/tests/parcel_fuzzer/main.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index ede0e925ee..192f9d5dce 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -96,7 +96,7 @@ void doReadFuzz(const char* backend, const std::vector>& reads, RandomParcelOptions options; P p; - fillRandomParcel(&p, std::move(provider), &options); + fillRandomParcel(&p, std::move(provider), &options); // consumes provider // since we are only using a byte to index CHECK_LE(reads.size(), 255u) << reads.size(); @@ -120,9 +120,7 @@ template void doReadWriteFuzz(const char* backend, const std::vector>& reads, const std::vector>& writes, FuzzedDataProvider&& provider) { RandomParcelOptions options; - P p; - fillRandomParcel(&p, std::move(provider), &options); // since we are only using a byte to index CHECK_LE(reads.size() + writes.size(), 255u) << reads.size(); -- cgit v1.2.3-59-g8ed1b From 608524d462278c2c9f6716cd94f126c85e9f2e91 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 2 Oct 2024 01:00:23 +0000 Subject: libbinder: Parcel: grow rejects large data pos This is unexpected behavior so throw an error. Allocating this much memory may cause OOM or other issues. Bug: 370831157 Test: fuzzer Change-Id: Iea0884ca61b08e52e6a6e9c66693e427cb5536f4 --- libs/binder/Parcel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'libs') diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 37113629a8..3d36f2eed7 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -2948,6 +2948,14 @@ status_t Parcel::growData(size_t len) return BAD_VALUE; } + if (mDataPos > mDataSize) { + // b/370831157 - this case used to abort. We also don't expect mDataPos < mDataSize, but + // this would only waste a bit of memory, so it's okay. + ALOGE("growData only expected at the end of a Parcel. pos: %zu, size: %zu, capacity: %zu", + mDataPos, len, mDataCapacity); + return BAD_VALUE; + } + if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow size_t newSize = ((mDataSize+len)*3)/2; -- cgit v1.2.3-59-g8ed1b From c54dad65317f851ce9d016bd90ec6a7a04da09fc Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 2 Oct 2024 00:37:59 +0000 Subject: libbinder: Parcel: validate read data before write This is slow, but it's required to prevent memory corruption. Ignore-AOSP-First: security Bug: 370840874 Test: fuzzer Change-Id: Ibc5566ade0389221690dc90324f93394cf7fc9a5 --- libs/binder/Parcel.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'libs') diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 3d36f2eed7..d346ad15d2 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -1211,6 +1211,10 @@ restart_write: //printf("Writing %ld bytes, padded to %ld\n", len, padded); uint8_t* const data = mData+mDataPos; + if (status_t status = validateReadData(mDataPos + padded); status != OK) { + return nullptr; // drops status + } + // Need to pad at end? if (padded != len) { #if BYTE_ORDER == BIG_ENDIAN @@ -1799,6 +1803,10 @@ status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity; if (enoughData && enoughObjects) { restart_write: + if (status_t status = validateReadData(mDataPos + sizeof(val)); status != OK) { + return status; + } + *reinterpret_cast(mData+mDataPos) = val; // remember if it's a file descriptor @@ -2042,6 +2050,10 @@ status_t Parcel::writeAligned(T val) { if ((mDataPos+sizeof(val)) <= mDataCapacity) { restart_write: + if (status_t status = validateReadData(mDataPos + sizeof(val)); status != OK) { + return status; + } + memcpy(mData + mDataPos, &val, sizeof(val)); return finishWrite(sizeof(val)); } -- cgit v1.2.3-59-g8ed1b From f99572ae6cb4cc717a7f716f116daef148ee901d Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 2 Oct 2024 01:19:56 +0000 Subject: libbinder: remove writeUnpadded Unused. Ignore-AOSP-First: security related Bug: 328161314 Test: build Change-Id: I751b8e23c02967dc422f5cd8f696e297bcc5c051 --- libs/binder/Parcel.cpp | 25 ------------------------- libs/binder/include/binder/Parcel.h | 1 - 2 files changed, 26 deletions(-) (limited to 'libs') diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 37113629a8..37c22de0a1 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -1150,31 +1150,6 @@ status_t Parcel::finishWrite(size_t len) return NO_ERROR; } -status_t Parcel::writeUnpadded(const void* data, size_t len) -{ - if (len > INT32_MAX) { - // don't accept size_t values which may have come from an - // inadvertent conversion from a negative int. - return BAD_VALUE; - } - - size_t end = mDataPos + len; - if (end < mDataPos) { - // integer overflow - return BAD_VALUE; - } - - if (end <= mDataCapacity) { -restart_write: - memcpy(mData+mDataPos, data, len); - return finishWrite(len); - } - - status_t err = growData(len); - if (err == NO_ERROR) goto restart_write; - return err; -} - status_t Parcel::write(const void* data, size_t len) { if (len > INT32_MAX) { diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index e2b23be58f..c48fe3db0f 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -172,7 +172,6 @@ public: LIBBINDER_EXPORTED status_t write(const void* data, size_t len); LIBBINDER_EXPORTED void* writeInplace(size_t len); - LIBBINDER_EXPORTED status_t writeUnpadded(const void* data, size_t len); LIBBINDER_EXPORTED status_t writeInt32(int32_t val); LIBBINDER_EXPORTED status_t writeUint32(uint32_t val); LIBBINDER_EXPORTED status_t writeInt64(int64_t val); -- cgit v1.2.3-59-g8ed1b From 3b685aa34bc16feec26a5f0cdfcd64f01c8cf4cc Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 2 Oct 2024 18:37:18 +0000 Subject: libbinder: better object logs Separate patch, b/c won't be backported Bug: 370840874 Test: N/A Ignore-AOSP-First: security related Change-Id: Iefc49398bab70e7255346dd4a0375b11edc1c159 --- libs/binder/Parcel.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 37113629a8..734b4b6129 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -180,7 +180,7 @@ static void acquire_object(const sp& proc, const flat_binder_objec } } - ALOGD("Invalid object type 0x%08x", obj.hdr.type); + ALOGE("Invalid object type 0x%08x to acquire", obj.hdr.type); } static void release_object(const sp& proc, const flat_binder_object& obj, @@ -210,7 +210,7 @@ static void release_object(const sp& proc, const flat_binder_objec } } - ALOGE("Invalid object type 0x%08x", obj.hdr.type); + ALOGE("Invalid object type 0x%08x to release", obj.hdr.type); } #endif // BINDER_WITH_KERNEL_IPC @@ -1874,7 +1874,10 @@ status_t Parcel::validateReadData(size_t upperBound) const if (mDataPos < kernelFields->mObjects[nextObject] + sizeof(flat_binder_object)) { // Requested info overlaps with an object if (!mServiceFuzzing) { - ALOGE("Attempt to read from protected data in Parcel %p", this); + ALOGE("Attempt to read or write from protected data in Parcel %p. pos: " + "%zu, nextObject: %zu, object offset: %llu, object size: %zu", + this, mDataPos, nextObject, kernelFields->mObjects[nextObject], + sizeof(flat_binder_object)); } return PERMISSION_DENIED; } -- cgit v1.2.3-59-g8ed1b From dfee5a2c360f4660dfad53bfd06d7923f98e242e Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Thu, 3 Oct 2024 03:42:49 +0000 Subject: Fix rounding error when constructing Rect from FloatRect Current rounding logic does not handle negative values correctly. FloatRect(-1080,...) is converted to Rect(-1079,...) Fix this by using the standard rounding function. Flag: EXEMPT build error (b/350967139) Bug: 310950423 Test: presubmit Change-Id: Ib8c5c393040149225e5b2bc858c21894e306a456 --- libs/ui/include/ui/Rect.h | 10 ++++------ libs/ui/tests/Rect_test.cpp | 10 ++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h index 2eb9330cc9..2307b44eb2 100644 --- a/libs/ui/include/ui/Rect.h +++ b/libs/ui/include/ui/Rect.h @@ -74,12 +74,10 @@ public: } inline explicit Rect(const FloatRect& floatRect) { - // Ideally we would use std::round, but we don't want to add an STL - // dependency here, so we use an approximation - left = static_cast(floatRect.left + 0.5f); - top = static_cast(floatRect.top + 0.5f); - right = static_cast(floatRect.right + 0.5f); - bottom = static_cast(floatRect.bottom + 0.5f); + left = static_cast(std::round(floatRect.left)); + top = static_cast(std::round(floatRect.top)); + right = static_cast(std::round(floatRect.right)); + bottom = static_cast(std::round(floatRect.bottom)); } inline explicit Rect(const ui::Size& size) { diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp index 9cc36bb15b..c3c8bd9fa4 100644 --- a/libs/ui/tests/Rect_test.cpp +++ b/libs/ui/tests/Rect_test.cpp @@ -99,6 +99,16 @@ TEST(RectTest, constructFromFloatRect) { EXPECT_EQ(30, rect.right); EXPECT_EQ(40, rect.bottom); } + + EXPECT_EQ(Rect(0, 1, -1, 0), Rect(FloatRect(0.f, 1.f, -1.f, 0.f))); + EXPECT_EQ(Rect(100000, 100000, -100000, -100000), + Rect(FloatRect(100000.f, 100000.f, -100000.f, -100000.f))); + + // round down if < .5 + EXPECT_EQ(Rect(0, 1, -1, 0), Rect(FloatRect(0.4f, 1.1f, -1.499f, 0.1f))); + + // round up if >= .5 + EXPECT_EQ(Rect(20, 20, -20, -20), Rect(FloatRect(19.5f, 19.9f, -19.5f, -19.9f))); } TEST(RectTest, makeInvalid) { -- cgit v1.2.3-59-g8ed1b From a9123c8b69422dfff377e3b07746dec48e91dc60 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Thu, 3 Oct 2024 03:56:44 +0000 Subject: Support floating point values for layer crop Flag: EXEMPT bug fix Fixes: 310950423 Test: presubmit Change-Id: I05feb4881a95bc8caad90a3d632b3c7881909bf3 --- libs/gui/LayerState.cpp | 12 +++- libs/gui/SurfaceComposerClient.cpp | 5 ++ libs/gui/include/gui/LayerState.h | 2 +- libs/gui/include/gui/SurfaceComposerClient.h | 1 + libs/gui/tests/SamplingDemo.cpp | 8 ++- libs/ui/include/ui/FloatRect.h | 3 + .../compositionengine/LayerFECompositionState.h | 2 +- .../CompositionEngine/src/OutputLayer.cpp | 64 +++++++++++----------- .../CompositionEngine/tests/OutputLayerTest.cpp | 9 +-- services/surfaceflinger/FrontEnd/LayerSnapshot.h | 2 +- .../FrontEnd/LayerSnapshotBuilder.cpp | 17 +++--- .../FrontEnd/RequestedLayerState.cpp | 8 +-- .../surfaceflinger/FrontEnd/RequestedLayerState.h | 2 +- services/surfaceflinger/Layer.cpp | 6 +- services/surfaceflinger/Layer.h | 6 +- services/surfaceflinger/LayerProtoHelper.cpp | 11 +++- services/surfaceflinger/LayerProtoHelper.h | 1 + .../Tracing/TransactionProtoParser.cpp | 2 +- .../tests/common/LayerLifecycleManagerHelper.h | 4 +- .../tests/unittests/CompositionTest.cpp | 7 ++- .../tests/unittests/LayerSnapshotTest.cpp | 4 +- .../tests/unittests/TransactionApplicationTest.cpp | 2 +- 22 files changed, 105 insertions(+), 73 deletions(-) (limited to 'libs') diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index b10996951b..422c57bc87 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -69,7 +69,7 @@ layer_state_t::layer_state_t() color(0), bufferTransform(0), transformToDisplayInverse(false), - crop(Rect::INVALID_RECT), + crop({0, 0, -1, -1}), dataspace(ui::Dataspace::UNKNOWN), surfaceDamageRegion(), api(-1), @@ -109,7 +109,10 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeUint32, flags); SAFE_PARCEL(output.writeUint32, mask); SAFE_PARCEL(matrix.write, output); - SAFE_PARCEL(output.write, crop); + SAFE_PARCEL(output.writeFloat, crop.top); + SAFE_PARCEL(output.writeFloat, crop.left); + SAFE_PARCEL(output.writeFloat, crop.bottom); + SAFE_PARCEL(output.writeFloat, crop.right); SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl); SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild); SAFE_PARCEL(output.writeFloat, color.r); @@ -218,7 +221,10 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readUint32, &mask); SAFE_PARCEL(matrix.read, input); - SAFE_PARCEL(input.read, crop); + SAFE_PARCEL(input.readFloat, &crop.top); + SAFE_PARCEL(input.readFloat, &crop.left); + SAFE_PARCEL(input.readFloat, &crop.bottom); + SAFE_PARCEL(input.readFloat, &crop.right); SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl); SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index df58df43be..21a375ec1c 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1652,6 +1652,11 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMatri SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop( const sp& sc, const Rect& crop) { + return setCrop(sc, crop.toFloatRect()); +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop( + const sp& sc, const FloatRect& crop) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 2cdde3255e..00065c81d8 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -330,7 +330,7 @@ struct layer_state_t { Region transparentRegion; uint32_t bufferTransform; bool transformToDisplayInverse; - Rect crop; + FloatRect crop; std::shared_ptr bufferData = nullptr; ui::Dataspace dataspace; HdrMetadata hdrMetadata; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 4f9af16826..2b99c0feb3 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -549,6 +549,7 @@ public: Transaction& setMatrix(const sp& sc, float dsdx, float dtdx, float dtdy, float dsdy); Transaction& setCrop(const sp& sc, const Rect& crop); + Transaction& setCrop(const sp& sc, const FloatRect& crop); Transaction& setCornerRadius(const sp& sc, float cornerRadius); Transaction& setBackgroundBlurRadius(const sp& sc, int backgroundBlurRadius); diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp index f98437b4f8..8fea689c91 100644 --- a/libs/gui/tests/SamplingDemo.cpp +++ b/libs/gui/tests/SamplingDemo.cpp @@ -46,7 +46,8 @@ public: SurfaceComposerClient::Transaction{} .setLayer(mButton, 0x7fffffff) - .setCrop(mButton, {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING}) + .setCrop(mButton, + Rect{0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING}) .setPosition(mButton, samplingArea.left + BUTTON_PADDING, samplingArea.top + BUTTON_PADDING) .setColor(mButton, half3{1, 1, 1}) @@ -59,7 +60,8 @@ public: SurfaceComposerClient::Transaction{} .setLayer(mButtonBlend, 0x7ffffffe) .setCrop(mButtonBlend, - {0, 0, width - 2 * SAMPLE_AREA_PADDING, height - 2 * SAMPLE_AREA_PADDING}) + Rect{0, 0, width - 2 * SAMPLE_AREA_PADDING, + height - 2 * SAMPLE_AREA_PADDING}) .setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING, samplingArea.top + SAMPLE_AREA_PADDING) .setColor(mButtonBlend, half3{1, 1, 1}) @@ -75,7 +77,7 @@ public: SurfaceComposerClient::Transaction{} .setLayer(mSamplingArea, 0x7ffffffd) - .setCrop(mSamplingArea, {0, 0, 100, 32}) + .setCrop(mSamplingArea, Rect{0, 0, 100, 32}) .setPosition(mSamplingArea, 490, 1606) .setColor(mSamplingArea, half3{0, 1, 0}) .setAlpha(mSamplingArea, 0.1) diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h index 4c9c7b7ef1..4366db539f 100644 --- a/libs/ui/include/ui/FloatRect.h +++ b/libs/ui/include/ui/FloatRect.h @@ -51,6 +51,9 @@ public: float bottom = 0.0f; constexpr bool isEmpty() const { return !(left < right && top < bottom); } + + // a valid rectangle has a non negative width and height + inline bool isValid() const { return (getWidth() >= 0) && (getHeight() >= 0); } }; inline bool operator==(const FloatRect& a, const FloatRect& b) { diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index d1429a2ec6..14a8fd6ad7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -155,7 +155,7 @@ struct LayerFECompositionState { uint32_t geomBufferTransform{0}; Rect geomBufferSize; Rect geomContentCrop; - Rect geomCrop; + FloatRect geomCrop; GenericLayerMetadataMap metadata; diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 091c207464..8e5c99126d 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -24,6 +24,7 @@ #include #include #include "system/graphics-base-v1.0.h" +#include "ui/FloatRect.h" #include @@ -185,35 +186,35 @@ Rect OutputLayer::calculateOutputDisplayFrame() const { const auto& layerState = *getLayerFE().getCompositionState(); const auto& outputState = getOutput().getState(); + // Convert from layer space to layerStackSpace // apply the layer's transform, followed by the display's global transform // here we're guaranteed that the layer's transform preserves rects - Region activeTransparentRegion = layerState.transparentRegionHint; const ui::Transform& layerTransform = layerState.geomLayerTransform; - const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; - const Rect& bufferSize = layerState.geomBufferSize; - Rect activeCrop = layerState.geomCrop; - if (!activeCrop.isEmpty() && bufferSize.isValid()) { - activeCrop = layerTransform.transform(activeCrop); - if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) { - activeCrop.clear(); - } - activeCrop = inverseLayerTransform.transform(activeCrop, true); - // This needs to be here as transform.transform(Rect) computes the - // transformed rect and then takes the bounding box of the result before - // returning. This means - // transform.inverse().transform(transform.transform(Rect)) != Rect - // in which case we need to make sure the final rect is clipped to the - // display bounds. - if (!activeCrop.intersect(bufferSize, &activeCrop)) { - activeCrop.clear(); - } + Region activeTransparentRegion = layerTransform.transform(layerState.transparentRegionHint); + if (!layerState.geomCrop.isEmpty() && layerState.geomBufferSize.isValid()) { + FloatRect activeCrop = layerTransform.transform(layerState.geomCrop); + activeCrop = activeCrop.intersect(outputState.layerStackSpace.getContent().toFloatRect()); + const FloatRect& bufferSize = + layerTransform.transform(layerState.geomBufferSize.toFloatRect()); + activeCrop = activeCrop.intersect(bufferSize); + // mark regions outside the crop as transparent - activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top)); - activeTransparentRegion.orSelf( - Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight())); - activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom)); - activeTransparentRegion.orSelf( - Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom)); + Rect topRegion = Rect(layerTransform.transform( + FloatRect(0, 0, layerState.geomBufferSize.getWidth(), layerState.geomCrop.top))); + Rect bottomRegion = Rect(layerTransform.transform( + FloatRect(0, layerState.geomCrop.bottom, layerState.geomBufferSize.getWidth(), + layerState.geomBufferSize.getHeight()))); + Rect leftRegion = Rect(layerTransform.transform(FloatRect(0, layerState.geomCrop.top, + layerState.geomCrop.left, + layerState.geomCrop.bottom))); + Rect rightRegion = Rect(layerTransform.transform( + FloatRect(layerState.geomCrop.right, layerState.geomCrop.top, + layerState.geomBufferSize.getWidth(), layerState.geomCrop.bottom))); + + activeTransparentRegion.orSelf(topRegion); + activeTransparentRegion.orSelf(bottomRegion); + activeTransparentRegion.orSelf(leftRegion); + activeTransparentRegion.orSelf(rightRegion); } // reduce uses a FloatRect to provide more accuracy during the @@ -229,13 +230,14 @@ Rect OutputLayer::calculateOutputDisplayFrame() const { geomLayerBounds.right += outset; geomLayerBounds.bottom += outset; } - Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))}; - if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) { - frame.clear(); - } - const ui::Transform displayTransform{outputState.transform}; - return displayTransform.transform(frame); + geomLayerBounds = layerTransform.transform(geomLayerBounds); + FloatRect frame = reduce(geomLayerBounds, activeTransparentRegion); + frame = frame.intersect(outputState.layerStackSpace.getContent().toFloatRect()); + + // convert from layerStackSpace to displaySpace + const ui::Transform displayTransform{outputState.transform}; + return Rect(displayTransform.transform(frame)); } uint32_t OutputLayer::calculateOutputRelativeBufferTransform( diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 1c54469cc0..95cdcd5b8d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -30,6 +30,7 @@ #include "MockHWC2.h" #include "MockHWComposer.h" #include "RegionMatcher.h" +#include "ui/FloatRect.h" #include @@ -270,7 +271,7 @@ struct OutputLayerDisplayFrameTest : public OutputLayerTest { mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT}; mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080}; mLayerFEState.geomBufferUsesDisplayInverseTransform = false; - mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080}; + mLayerFEState.geomCrop = FloatRect{0, 0, 1920, 1080}; mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080}); @@ -296,20 +297,20 @@ TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame } TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) { - mLayerFEState.geomCrop = Rect{100, 200, 300, 500}; + mLayerFEState.geomCrop = FloatRect{100, 200, 300, 500}; const Rect expected{100, 200, 300, 500}; EXPECT_THAT(calculateOutputDisplayFrame(), expected); } TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) { - mLayerFEState.geomCrop = Rect{100, 200, 300, 500}; + mLayerFEState.geomCrop = FloatRect{100, 200, 300, 500}; mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080); const Rect expected{1420, 100, 1720, 300}; EXPECT_THAT(calculateOutputDisplayFrame(), expected); } TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) { - mLayerFEState.geomCrop = Rect{}; + mLayerFEState.geomCrop = FloatRect{}; const Rect expected{0, 0, 1920, 1080}; EXPECT_THAT(calculateOutputDisplayFrame(), expected); } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index 398e64a4f1..b7d4cc5d06 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -72,7 +72,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { bool premultipliedAlpha; ui::Transform parentTransform; Rect bufferSize; - Rect croppedBufferSize; + FloatRect croppedBufferSize; std::shared_ptr externalTexture; gui::LayerMetadata layerMetadata; gui::LayerMetadata relativeLayerMetadata; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index ee605b7a3e..10e212e7b5 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -116,7 +116,7 @@ ui::Transform getInputTransform(const LayerSnapshot& snapshot) { * that's already included. */ std::pair getInputBounds(const LayerSnapshot& snapshot, bool fillParentBounds) { - FloatRect inputBounds = snapshot.croppedBufferSize.toFloatRect(); + FloatRect inputBounds = snapshot.croppedBufferSize; if (snapshot.hasBufferOrSidebandStream() && snapshot.croppedBufferSize.isValid() && snapshot.localTransform.getType() != ui::Transform::IDENTITY) { inputBounds = snapshot.localTransform.transform(inputBounds); @@ -220,7 +220,7 @@ void handleDropInputMode(LayerSnapshot& snapshot, const LayerSnapshot& parentSna } // Check if the parent has cropped the buffer - Rect bufferSize = snapshot.croppedBufferSize; + FloatRect bufferSize = snapshot.croppedBufferSize; if (!bufferSize.isValid()) { snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; return; @@ -970,7 +970,7 @@ void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot, parentRoundedCorner.radius.y *= t.getScaleY(); } - FloatRect layerCropRect = snapshot.croppedBufferSize.toFloatRect(); + FloatRect layerCropRect = snapshot.croppedBufferSize; const vec2 radius(requested.cornerRadius, requested.cornerRadius); RoundedCornerState layerSettings(layerCropRect, radius); const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty(); @@ -1061,7 +1061,7 @@ void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot, requested.externalTexture ? snapshot.bufferSize.toFloatRect() : parentBounds; snapshot.geomLayerCrop = parentBounds; if (!requested.crop.isEmpty()) { - snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop.toFloatRect()); + snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop); } snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(snapshot.geomLayerCrop); snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds); @@ -1072,10 +1072,10 @@ void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot, snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion); snapshot.parentTransform = parentSnapshot.geomLayerTransform; - // Subtract the transparent region and snap to the bounds - const Rect bounds = - RequestedLayerState::reduce(snapshot.croppedBufferSize, requested.transparentRegion); if (requested.potentialCursor) { + // Subtract the transparent region and snap to the bounds + const Rect bounds = RequestedLayerState::reduce(Rect(snapshot.croppedBufferSize), + requested.transparentRegion); snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds); } } @@ -1192,7 +1192,8 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY; } - snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize(); + snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(), + snapshot.croppedBufferSize.getWidth()}; // If the layer is a clone, we need to crop the input region to cloned root to prevent // touches from going outside the cloned area. diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 5734ccf38f..1eff9d699c 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -96,7 +96,7 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) LLOGV(layerId, "Created %s flags=%d", getDebugString().c_str(), flags); color.a = 1.0f; - crop.makeInvalid(); + crop = {0, 0, -1, -1}; z = 0; layerStack = ui::DEFAULT_LAYER_STACK; transformToDisplayInverse = false; @@ -473,10 +473,10 @@ Rect RequestedLayerState::getBufferSize(uint32_t displayRotationFlags) const { return Rect(0, 0, static_cast(bufWidth), static_cast(bufHeight)); } -Rect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const { - Rect size = bufferSize; +FloatRect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const { + FloatRect size = bufferSize.toFloatRect(); if (!crop.isEmpty() && size.isValid()) { - size.intersect(crop, &size); + size = size.intersect(crop); } else if (!crop.isEmpty()) { size = crop; } diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 1d96dff336..3220e86ce4 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -79,7 +79,7 @@ struct RequestedLayerState : layer_state_t { bool isHiddenByPolicy() const; half4 getColor() const; Rect getBufferSize(uint32_t displayRotationFlags) const; - Rect getCroppedBufferSize(const Rect& bufferSize) const; + FloatRect getCroppedBufferSize(const Rect& bufferSize) const; Rect getBufferCrop() const; std::string getDebugString() const; std::string getDebugStringShort() const; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index dcb0812b67..9fdcbd0f1b 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -138,7 +138,7 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) { ALOGV("Creating Layer %s", getDebugName()); - mDrawingState.crop.makeInvalid(); + mDrawingState.crop = {0, 0, -1, -1}; mDrawingState.sequence = 0; mDrawingState.transform.set(0, 0); mDrawingState.frameNumber = 0; @@ -316,7 +316,7 @@ bool Layer::computeTrustedPresentationState(const FloatRect& bounds, const Float Rect Layer::getCroppedBufferSize(const State& s) const { Rect size = getBufferSize(s); - Rect crop = getCrop(s); + Rect crop = Rect(getCrop(s)); if (!crop.isEmpty() && size.isValid()) { size.intersect(crop, &size); } else if (!crop.isEmpty()) { @@ -373,7 +373,7 @@ void Layer::setTransactionFlags(uint32_t mask) { mTransactionFlags |= mask; } -bool Layer::setCrop(const Rect& crop) { +bool Layer::setCrop(const FloatRect& crop) { if (mDrawingState.crop == crop) return false; mDrawingState.sequence++; mDrawingState.crop = crop; diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 9bc557e917..57093ae602 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -95,7 +95,7 @@ public: struct State { int32_t sequence; // changes when visible regions can change // Crop is expressed in layer space coordinate. - Rect crop; + FloatRect crop; LayerMetadata metadata; ui::Dataspace dataspace; @@ -172,7 +172,7 @@ public: // be delayed until the resize completes. // Buffer space - bool setCrop(const Rect& crop); + bool setCrop(const FloatRect& crop); bool setTransform(uint32_t /*transform*/); bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/); @@ -198,7 +198,7 @@ public: Region getVisibleRegion(const DisplayDevice*) const; void updateLastLatchTime(nsecs_t latchtime); - Rect getCrop(const Layer::State& s) const { return s.crop; } + Rect getCrop(const Layer::State& s) const { return Rect(s.crop); } // from graphics API static ui::Dataspace translateDataspace(ui::Dataspace dataspace); diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 5eea45b436..a0fbab0728 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -106,6 +106,13 @@ void LayerProtoHelper::readFromProto(const perfetto::protos::RectProto& proto, R outRect.right = proto.right(); } +void LayerProtoHelper::readFromProto(const perfetto::protos::RectProto& proto, FloatRect& outRect) { + outRect.left = proto.left(); + outRect.top = proto.top(); + outRect.bottom = proto.bottom(); + outRect.right = proto.right(); +} + void LayerProtoHelper::writeToProto( const FloatRect& rect, std::function getFloatRectProto) { @@ -427,7 +434,7 @@ void LayerProtoHelper::writeSnapshotToProto(perfetto::protos::LayerProto* layerI layerInfo->mutable_color_transform()); } - LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(), + LayerProtoHelper::writeToProto(snapshot.croppedBufferSize, [&]() { return layerInfo->mutable_source_bounds(); }); LayerProtoHelper::writeToProto(snapshot.transformedBounds, [&]() { return layerInfo->mutable_screen_bounds(); }); @@ -455,7 +462,7 @@ void LayerProtoHelper::writeSnapshotToProto(perfetto::protos::LayerProto* layerI return layerInfo->mutable_requested_position(); }); - LayerProtoHelper::writeToProto(requestedState.crop, + LayerProtoHelper::writeToProto(Rect(requestedState.crop), [&]() { return layerInfo->mutable_crop(); }); layerInfo->set_is_opaque(snapshot.contentOpaque); diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 41ea68420f..3ca553a903 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -44,6 +44,7 @@ public: std::function getRectProto); static void writeToProto(const Rect& rect, perfetto::protos::RectProto* rectProto); static void readFromProto(const perfetto::protos::RectProto& proto, Rect& outRect); + static void readFromProto(const perfetto::protos::RectProto& proto, FloatRect& outRect); static void writeToProto(const FloatRect& rect, std::function getFloatRectProto); static void writeToProto(const Region& region, diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index b1895989ec..f39a4d2af4 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -147,7 +147,7 @@ perfetto::protos::LayerState TransactionProtoParser::toProto( proto.set_transform_to_display_inverse(layer.transformToDisplayInverse); } if (layer.what & layer_state_t::eCropChanged) { - LayerProtoHelper::writeToProto(layer.crop, proto.mutable_crop()); + LayerProtoHelper::writeToProto(Rect(layer.crop), proto.mutable_crop()); } if (layer.what & layer_state_t::eBufferChanged) { perfetto::protos::LayerState_BufferData* bufferProto = proto.mutable_buffer_data(); diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h index ae380ad459..b472047bd6 100644 --- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -182,7 +182,7 @@ public: mLifecycleManager.applyTransactions(setZTransaction(id, z)); } - void setCrop(uint32_t id, const Rect& crop) { + void setCrop(uint32_t id, const FloatRect& crop) { std::vector transactions; transactions.emplace_back(); transactions.back().states.push_back({}); @@ -193,6 +193,8 @@ public: mLifecycleManager.applyTransactions(transactions); } + void setCrop(uint32_t id, const Rect& crop) { setCrop(id, crop.toFloatRect()); } + void setFlags(uint32_t id, uint32_t mask, uint32_t flags) { std::vector transactions; transactions.emplace_back(); diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 23d3c168bd..4f72424bd7 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -467,7 +467,7 @@ struct BaseLayerProperties { LayerProperties::FORMAT, LayerProperties::USAGE | GraphicBuffer::USAGE_HW_TEXTURE); - layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); + layer.crop = FloatRect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); layer.externalTexture = buffer; layer.bufferData->acquireFence = Fence::NO_FENCE; layer.dataspace = ui::Dataspace::UNKNOWN; @@ -664,7 +664,8 @@ struct SidebandLayerProperties : public BaseLayerProperties(DEFAULT_SIDEBAND_STREAM), false); layer.sidebandStream = stream; - layer.crop = Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH); + layer.crop = + FloatRect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH); } static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) { @@ -828,7 +829,7 @@ struct EffectLayerVariant : public BaseLayerVariant { return frontend::RequestedLayerState(args); }); - layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); + layer.crop = FloatRect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); return layer; } diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 75d2fa3c7f..a35ae15c03 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -162,12 +162,12 @@ TEST_F(LayerSnapshotTest, croppedByParent) { info.info.logicalHeight = 100; info.info.logicalWidth = 200; mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info); - Rect layerCrop(0, 0, 10, 20); + FloatRect layerCrop(0, 0, 10, 20); setCrop(11, layerCrop); EXPECT_TRUE(mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Geometry)); UPDATE_AND_VERIFY_WITH_DISPLAY_CHANGES(mSnapshotBuilder, STARTING_ZORDER); EXPECT_EQ(getSnapshot(11)->geomCrop, layerCrop); - EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop.toFloatRect()); + EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop); float maxHeight = static_cast(info.info.logicalHeight * 10); float maxWidth = static_cast(info.info.logicalWidth * 10); diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index fab1f6d451..1e8cd0a0ca 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -387,7 +387,7 @@ public: state.state.what = what; if (what & layer_state_t::eCropChanged) { - state.state.crop = Rect(1, 2, 3, 4); + state.state.crop = FloatRect(1, 2, 3, 4); } if (what & layer_state_t::eFlagsChanged) { state.state.flags = layer_state_t::eEnableBackpressure; -- cgit v1.2.3-59-g8ed1b From 6aa3b8931d7ffc8306b22358bb0dd4b2dc646b49 Mon Sep 17 00:00:00 2001 From: Lloyd Pique Date: Wed, 2 Oct 2024 13:44:41 -0700 Subject: FTL: Allow implicit conversions with NonNull C++ allows certain implicit conversions for pointers, such as the implicit conversion of "T*" to "const T*". NonNull did not allow them, but that is easily corrected. Bug: 185536303 Flag: EXEMPT Relaxes a compile-time constraint Test: atest ftl_test Change-Id: I0c14805c8fdcca979c67d2c86d57e5e678aa1b32 --- include/ftl/non_null.h | 9 +++++++++ libs/ftl/non_null_test.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) (limited to 'libs') diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h index 4a5d8bffd0..e68428810b 100644 --- a/include/ftl/non_null.h +++ b/include/ftl/non_null.h @@ -68,6 +68,15 @@ class NonNull final { constexpr NonNull(const NonNull&) = default; constexpr NonNull& operator=(const NonNull&) = default; + template >> + constexpr NonNull(const NonNull& other) : pointer_(other.get()) {} + + template >> + constexpr NonNull& operator=(const NonNull& other) { + pointer_ = other.get(); + return *this; + } + [[nodiscard]] constexpr const Pointer& get() const { return pointer_; } [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); } diff --git a/libs/ftl/non_null_test.cpp b/libs/ftl/non_null_test.cpp index 367b398915..457237c35e 100644 --- a/libs/ftl/non_null_test.cpp +++ b/libs/ftl/non_null_test.cpp @@ -81,6 +81,31 @@ static_assert(static_cast(kApplePtr)); static_assert(std::is_same_v())), ftl::NonNull>); +class Base {}; +class Derived : public Base {}; + +static_assert(std::is_constructible_v, ftl::NonNull>); +static_assert(!std::is_constructible_v, ftl::NonNull>); +static_assert(std::is_constructible_v, ftl::NonNull>); +static_assert(!std::is_constructible_v, ftl::NonNull>); +static_assert(std::is_constructible_v, ftl::NonNull>); +static_assert(!std::is_constructible_v, ftl::NonNull>); +static_assert(std::is_constructible_v>, + ftl::NonNull>>); +static_assert(std::is_constructible_v>, + ftl::NonNull>>); + +static_assert(std::is_assignable_v, ftl::NonNull>); +static_assert(!std::is_assignable_v, ftl::NonNull>); +static_assert(std::is_assignable_v, ftl::NonNull>); +static_assert(!std::is_assignable_v, ftl::NonNull>); +static_assert(std::is_assignable_v, ftl::NonNull>); +static_assert(!std::is_assignable_v, ftl::NonNull>); +static_assert(std::is_assignable_v>, + ftl::NonNull>>); +static_assert(std::is_assignable_v>, + ftl::NonNull>>); + } // namespace TEST(NonNull, SwapRawPtr) { @@ -156,4 +181,14 @@ TEST(NonNull, UnorderedSetOfSmartPtr) { EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } +TEST(NonNull, ImplicitConversion) { + int i = 123; + int j = 345; + auto ip = ftl::as_non_null(&i); + ftl::NonNull vp{ip}; + EXPECT_EQ(vp.get(), &i); + vp = ftl::as_non_null(&j); + EXPECT_EQ(vp.get(), &j); +} + } // namespace android::test -- cgit v1.2.3-59-g8ed1b From 117556e09150a155c80385b315d80fb0a762416d Mon Sep 17 00:00:00 2001 From: Melody Hsu Date: Thu, 3 Oct 2024 21:58:56 +0000 Subject: Remove unecessary flag checks for Transaction#setFlags The if statement does not provide any value and is an additional area to have to maintain when introducing new flags. Bug: 371381313 Test: presubmit Flag: EXEMPT bug fix Change-Id: Ie1a929451aaa6d87e82e9ab131ddb50d2d70cee3 --- libs/gui/SurfaceComposerClient.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'libs') diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index df58df43be..b976d814b6 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1539,14 +1539,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags mStatus = BAD_INDEX; return *this; } - 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::eIgnoreDestinationFrame) || - (mask & layer_state_t::eLayerIsDisplayDecoration) || - (mask & layer_state_t::eLayerIsRefreshRateIndicator)) { - s->what |= layer_state_t::eFlagsChanged; - } + s->what |= layer_state_t::eFlagsChanged; s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; -- cgit v1.2.3-59-g8ed1b From 95f669a30a74fa519dc9dc760a099d977ebee567 Mon Sep 17 00:00:00 2001 From: Sally Qi Date: Tue, 27 Aug 2024 11:31:42 -0700 Subject: [Lut HAL backend] implementation - Add Lut in HWComposer::DeviceRequestedChanges - parse lutProperties into overlayProperties - Add a mapper to store lut fd to avoid dup() ops if passing the fds into callees. - SurfaceComposerClient::Transaction::setLuts interface Bug: 329472100 Test: builds Flag: NONE HAL backend interface change Change-Id: Ib2993ce1eab66ab456388c0d15b032201eac7e91 --- libs/gui/SurfaceComposerClient.cpp | 14 ++++++++++ libs/gui/aidl/android/gui/Lut.aidl | 30 ++++++++++++++++++++ libs/gui/aidl/android/gui/LutProperties.aidl | 32 ++++++++++++++++++++++ libs/gui/aidl/android/gui/OverlayProperties.aidl | 4 +++ libs/gui/include/gui/SurfaceComposerClient.h | 5 ++++ .../include/compositionengine/OutputLayer.h | 4 +++ .../include/compositionengine/impl/Display.h | 2 ++ .../include/compositionengine/impl/OutputLayer.h | 2 ++ .../include/compositionengine/mock/OutputLayer.h | 3 ++ .../CompositionEngine/src/Display.cpp | 20 ++++++++++++++ .../CompositionEngine/src/OutputLayer.cpp | 7 +++++ .../CompositionEngine/tests/DisplayTest.cpp | 4 +++ .../CompositionEngine/tests/MockHWComposer.h | 5 +--- .../DisplayHardware/AidlComposerHal.cpp | 8 +++++- .../DisplayHardware/AidlComposerHal.h | 2 +- .../surfaceflinger/DisplayHardware/ComposerHal.h | 2 +- services/surfaceflinger/DisplayHardware/HWC2.cpp | 30 ++++++++++++++------ services/surfaceflinger/DisplayHardware/HWC2.h | 16 +++++++---- .../surfaceflinger/DisplayHardware/HWComposer.cpp | 27 ++++++++---------- .../surfaceflinger/DisplayHardware/HWComposer.h | 22 +++++++-------- .../DisplayHardware/HidlComposerHal.cpp | 3 +- .../DisplayHardware/HidlComposerHal.h | 2 +- services/surfaceflinger/SurfaceFlinger.cpp | 16 +++++++++++ .../unittests/mock/DisplayHardware/MockComposer.h | 2 +- .../unittests/mock/DisplayHardware/MockHWC2.h | 2 +- 25 files changed, 212 insertions(+), 52 deletions(-) create mode 100644 libs/gui/aidl/android/gui/Lut.aidl create mode 100644 libs/gui/aidl/android/gui/LutProperties.aidl (limited to 'libs') diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index df58df43be..10746e07f8 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1941,6 +1941,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesir return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts( + const sp& sc, const base::unique_fd& /*lutFd*/, + const std::vector& /*offsets*/, const std::vector& /*dimensions*/, + const std::vector& /*sizes*/, const std::vector& /*samplingKeys*/) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + // TODO (b/329472856): update layer_state_t for lut(s) + registerSurfaceControlForCallback(sc); + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachingHint( const sp& sc, gui::CachingHint cachingHint) { layer_state_t* s = getLayerState(sc); diff --git a/libs/gui/aidl/android/gui/Lut.aidl b/libs/gui/aidl/android/gui/Lut.aidl new file mode 100644 index 0000000000..a06e521789 --- /dev/null +++ b/libs/gui/aidl/android/gui/Lut.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.LutProperties; +import android.os.ParcelFileDescriptor; + +/** + * This mirrors aidl::android::hardware::graphics::composer3::Lut definition + * @hide + */ +parcelable Lut { + @nullable ParcelFileDescriptor pfd; + + LutProperties lutProperties; +} \ No newline at end of file diff --git a/libs/gui/aidl/android/gui/LutProperties.aidl b/libs/gui/aidl/android/gui/LutProperties.aidl new file mode 100644 index 0000000000..561e0c069c --- /dev/null +++ b/libs/gui/aidl/android/gui/LutProperties.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** + * This mirrors aidl::android::hardware::graphics::composer3::LutProperties definition. + * @hide + */ +parcelable LutProperties { + @Backing(type="int") + enum Dimension { ONE_D = 1, THREE_D = 3 } + Dimension dimension; + + long size; + @Backing(type="int") + enum SamplingKey { RGB, MAX_RGB } + SamplingKey[] samplingKeys; +} \ No newline at end of file diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl index 5fb1a83c65..7f41bdab2f 100644 --- a/libs/gui/aidl/android/gui/OverlayProperties.aidl +++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl @@ -16,6 +16,8 @@ package android.gui; +import android.gui.LutProperties; + /** @hide */ parcelable OverlayProperties { parcelable SupportedBufferCombinations { @@ -27,4 +29,6 @@ parcelable OverlayProperties { SupportedBufferCombinations[] combinations; boolean supportMixedColorSpaces; + + @nullable LutProperties[] lutProperties; } diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 4f9af16826..88c7e9300d 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -601,6 +601,11 @@ public: Transaction& setExtendedRangeBrightness(const sp& sc, float currentBufferRatio, float desiredRatio); Transaction& setDesiredHdrHeadroom(const sp& sc, float desiredRatio); + Transaction& setLuts(const sp& sc, const base::unique_fd& lutFd, + const std::vector& offsets, + const std::vector& dimensions, + const std::vector& sizes, + const std::vector& samplingKeys); Transaction& setCachingHint(const sp& sc, gui::CachingHint cachingHint); Transaction& setHdrMetadata(const sp& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp& sc, diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index 4dbf8d2fce..dcfe21a849 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -128,6 +128,10 @@ public: // Applies a HWC device layer request virtual void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) = 0; + // Applies a HWC device layer lut + virtual void applyDeviceLayerLut(aidl::android::hardware::graphics::composer3::LutProperties, + ndk::ScopedFileDescriptor) = 0; + // Returns true if the composition settings scale pixels virtual bool needsFiltering() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index d1eff241d3..a39abb40d2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -82,11 +82,13 @@ public: using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests; using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests; using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty; + using LayerLuts = android::HWComposer::DeviceRequestedChanges::LayerLuts; virtual bool allLayersRequireClientComposition() const; virtual void applyChangedTypesToLayers(const ChangedTypes&); virtual void applyDisplayRequests(const DisplayRequests&); virtual void applyLayerRequestsToLayers(const LayerRequests&); virtual void applyClientTargetRequests(const ClientTargetProperty&); + virtual void applyLayerLutsToLayers(const LayerLuts&); // Internal virtual void setConfiguration(const compositionengine::DisplayCreationArgs&); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index f383392e55..354a4416f2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -60,6 +60,8 @@ public: aidl::android::hardware::graphics::composer3::Composition) override; void prepareForDeviceLayerRequests() override; void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override; + void applyDeviceLayerLut(aidl::android::hardware::graphics::composer3::LutProperties, + ndk::ScopedFileDescriptor) override; bool needsFiltering() const override; std::optional getOverrideCompositionSettings() const override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 5fef63a510..48c2f9c483 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -56,6 +56,9 @@ public: MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request)); MOCK_CONST_METHOD0(needsFiltering, bool()); MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional()); + MOCK_METHOD(void, applyDeviceLayerLut, + (aidl::android::hardware::graphics::composer3::LutProperties, + ndk::ScopedFileDescriptor)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 77b1940e23..b0164b7c33 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -278,6 +278,7 @@ void Display::applyCompositionStrategy(const std::optionaldisplayRequests); applyLayerRequestsToLayers(changes->layerRequests); applyClientTargetRequests(changes->clientTargetProperty); + applyLayerLutsToLayers(changes->layerLuts); } // Determine what type of composition we are doing from the final state @@ -359,6 +360,25 @@ void Display::applyClientTargetRequests(const ClientTargetProperty& clientTarget static_cast(clientTargetProperty.clientTargetProperty.pixelFormat)); } +void Display::applyLayerLutsToLayers(const LayerLuts& layerLuts) { + auto& mapper = getCompositionEngine().getHwComposer().getLutFileDescriptorMapper(); + for (auto* layer : getOutputLayersOrderedByZ()) { + auto hwcLayer = layer->getHwcLayer(); + if (!hwcLayer) { + continue; + } + + if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) { + if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) { + layer->applyDeviceLayerLut(lutsIt->second, + ndk::ScopedFileDescriptor(mapperIt->second.release())); + } + } + } + + mapper.clear(); +} + void Display::executeCommands() { const auto halDisplayIdOpt = HalDisplayId::tryCast(mId); if (mIsDisconnected || !halDisplayIdOpt) { diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 091c207464..07bbb00073 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -37,6 +37,7 @@ #pragma clang diagnostic pop // ignored "-Wconversion" using aidl::android::hardware::graphics::composer3::Composition; +using aidl::android::hardware::graphics::composer3::LutProperties; namespace android::compositionengine { @@ -844,6 +845,12 @@ void OutputLayer::applyDeviceLayerRequest(hal::LayerRequest request) { } } +void OutputLayer::applyDeviceLayerLut(LutProperties /*lutProperties*/, + ndk::ScopedFileDescriptor /*lutPfd*/) { + // TODO(b/329472856): decode the shared memory of the pfd, and store the lut data into + // OutputLayerCompositionState#hwc struct +} + bool OutputLayer::needsFiltering() const { const auto& state = getState(); const auto& sourceCrop = state.sourceCrop; diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 39163ea60f..9c0e62c643 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -133,6 +133,7 @@ struct DisplayTestCommon : public testing::Test { MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&)); MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&)); MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&)); + MOCK_METHOD1(applyLayerLutsToLayers, void(const impl::Display::LayerLuts&)); const compositionengine::CompositionEngine& mCompositionEngine; impl::OutputCompositionState mState; @@ -212,6 +213,7 @@ struct PartialMockDisplayTestCommon : public DisplayTestCommon { aidl::android::hardware::graphics::common::Dataspace::UNKNOWN}, -1.f, DimmingStage::NONE}, + {}, }; void chooseCompositionStrategy(Display* display) { @@ -615,6 +617,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) { EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests)) .Times(1); EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); + EXPECT_CALL(*mDisplay, applyLayerLutsToLayers(mDeviceRequestedChanges.layerLuts)).Times(1); chooseCompositionStrategy(mDisplay.get()); @@ -667,6 +670,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) { EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests)) .Times(1); EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); + EXPECT_CALL(*mDisplay, applyLayerLutsToLayers(mDeviceRequestedChanges.layerLuts)).Times(1); chooseCompositionStrategy(mDisplay.get()); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index e910c72e2e..5c55ce739a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -152,10 +152,7 @@ public: getOverlaySupport, (), (const, override)); MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); - MOCK_METHOD(status_t, getRequestedLuts, - (PhysicalDisplayId, - std::vector*), - (override)); + MOCK_METHOD((HWC2::Display::LutFileDescriptorMapper&), getLutFileDescriptorMapper, (), ()); }; } // namespace mock diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 66237b9d8a..77bd8040c5 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -1547,7 +1547,8 @@ Error AidlComposer::getClientTargetProperty( return error; } -Error AidlComposer::getRequestedLuts(Display display, std::vector* outLuts) { +Error AidlComposer::getRequestedLuts(Display display, std::vector* outLayers, + std::vector* outLuts) { Error error = Error::NONE; mMutex.lock_shared(); if (auto reader = getReader(display)) { @@ -1556,6 +1557,11 @@ Error AidlComposer::getRequestedLuts(Display display, std::vectorreserve(outLuts->size()); + for (const auto& layerLut : *outLuts) { + outLayers->emplace_back(translate(layerLut.layer)); + } return error; } diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 246223a668..cdb67e4e5c 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -245,7 +245,7 @@ public: Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime, int32_t frameIntervalNs) override; Error getRequestedLuts( - Display display, + Display display, std::vector* outLayers, std::vector* outLuts) override; Error setLayerLuts( diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 7db9a94e54..09056631d0 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -305,7 +305,7 @@ public: virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0; virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime, int32_t frameIntervalNs) = 0; - virtual Error getRequestedLuts(Display display, + virtual Error getRequestedLuts(Display display, std::vector* outLayers, std::vector* outLuts) = 0; virtual Error setLayerLuts(Display display, Layer layer, std::vector& luts) = 0; }; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index f1fa9389eb..1df2ab12ce 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -609,17 +609,29 @@ Error Display::getClientTargetProperty( return static_cast(error); } -Error Display::getRequestedLuts(std::vector* outLayerLuts) { - std::vector tmpLayerLuts; - const auto error = mComposer.getRequestedLuts(mId, &tmpLayerLuts); - for (DisplayLuts::LayerLut& layerLut : tmpLayerLuts) { - if (layerLut.lut.pfd.get() >= 0) { - outLayerLuts->push_back({layerLut.layer, - Lut{ndk::ScopedFileDescriptor(layerLut.lut.pfd.release()), - layerLut.lut.lutProperties}}); +Error Display::getRequestedLuts(LayerLuts* outLuts, + LutFileDescriptorMapper& lutFileDescriptorMapper) { + std::vector layerIds; + std::vector tmpLuts; + const auto error = static_cast(mComposer.getRequestedLuts(mId, &layerIds, &tmpLuts)); + if (error != Error::NONE) { + return error; + } + + uint32_t numElements = layerIds.size(); + outLuts->clear(); + for (uint32_t i = 0; i < numElements; ++i) { + auto layer = getLayerById(layerIds[i]); + if (layer) { + auto& layerLut = tmpLuts[i]; + outLuts->emplace_or_replace(layer.get(), layerLut.lut.lutProperties); + lutFileDescriptorMapper.emplace_or_replace(layer.get(), + ndk::ScopedFileDescriptor( + layerLut.lut.pfd.release())); } } - return static_cast(error); + + return Error::NONE; } Error Display::getDisplayDecorationSupport( diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 8e2aeaf234..61f92f4d74 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,13 @@ public: virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0; virtual std::optional getPhysicalSizeInMm() const = 0; + static const int kLutFileDescriptorMapperSize = 20; + using LayerLuts = + ftl::SmallMap; + using LutFileDescriptorMapper = + ftl::SmallMap; + [[nodiscard]] virtual hal::Error acceptChanges() = 0; [[nodiscard]] virtual base::expected, hal::Error> createLayer() = 0; @@ -183,8 +191,7 @@ public: aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness* outClientTargetProperty) = 0; [[nodiscard]] virtual hal::Error getRequestedLuts( - std::vector* - outLuts) = 0; + LayerLuts* outLuts, LutFileDescriptorMapper& lutFileDescriptorMapper) = 0; [[nodiscard]] virtual hal::Error getDisplayDecorationSupport( std::optional* support) = 0; @@ -268,9 +275,8 @@ public: hal::Error getClientTargetProperty( aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness* outClientTargetProperty) override; - hal::Error getRequestedLuts( - std::vector* - outLuts) override; + hal::Error getRequestedLuts(LayerLuts* outLuts, + LutFileDescriptorMapper& lutFileDescriptorMapper) override; hal::Error getDisplayDecorationSupport( std::optional* support) override; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index bd093f52cf..edce805a7e 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -587,9 +587,14 @@ status_t HWComposer::getDeviceCompositionChanges( error = hwcDisplay->getClientTargetProperty(&clientTargetProperty); RETURN_IF_HWC_ERROR_FOR("getClientTargetProperty", error, displayId, BAD_INDEX); + DeviceRequestedChanges::LayerLuts layerLuts; + error = hwcDisplay->getRequestedLuts(&layerLuts, mLutFileDescriptorMapper); + RETURN_IF_HWC_ERROR_FOR("getRequestedLuts", error, displayId, BAD_INDEX); + outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests), std::move(layerRequests), - std::move(clientTargetProperty)}); + std::move(clientTargetProperty), + std::move(layerLuts)}); error = hwcDisplay->acceptChanges(); RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX); @@ -978,21 +983,6 @@ status_t HWComposer::getDisplayDecorationSupport( return NO_ERROR; } -status_t HWComposer::getRequestedLuts( - PhysicalDisplayId displayId, - std::vector* outLuts) { - RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - const auto error = mDisplayData[displayId].hwcDisplay->getRequestedLuts(outLuts); - if (error == hal::Error::UNSUPPORTED) { - RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); - } - if (error == hal::Error::BAD_PARAMETER) { - RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); - } - RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); - return NO_ERROR; -} - status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on); @@ -1036,6 +1026,11 @@ const std::unordered_map& HWComposer::getSupportedLayerGeneri return mSupportedLayerGenericMetadata; } +ftl::SmallMap& +HWComposer::getLutFileDescriptorMapper() { + return mLutFileDescriptorMapper; +} + void HWComposer::dumpOverlayProperties(std::string& result) const { // dump overlay properties result.append("OverlayProperties:\n"); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index b95c619f75..7b04d6755a 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -53,6 +53,7 @@ #include #include #include +#include #include namespace android { @@ -90,11 +91,14 @@ public: aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; using DisplayRequests = hal::DisplayRequest; using LayerRequests = std::unordered_map; + using LutProperties = aidl::android::hardware::graphics::composer3::LutProperties; + using LayerLuts = HWC2::Display::LayerLuts; ChangedTypes changedTypes; DisplayRequests displayRequests; LayerRequests layerRequests; ClientTargetProperty clientTargetProperty; + LayerLuts layerLuts; }; struct HWCDisplayMode { @@ -311,18 +315,15 @@ public: virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) = 0; - - // Composer 4.0 - virtual status_t getRequestedLuts( - PhysicalDisplayId, - std::vector*) = 0; + // mapper + virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, const android::HWComposer::DeviceRequestedChanges& rhs) { return lhs.changedTypes == rhs.changedTypes && lhs.displayRequests == rhs.displayRequests && lhs.layerRequests == rhs.layerRequests && - lhs.clientTargetProperty == rhs.clientTargetProperty; + lhs.clientTargetProperty == rhs.clientTargetProperty && lhs.layerLuts == rhs.layerLuts; } namespace impl { @@ -480,11 +481,8 @@ public: status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) override; - // Composer 4.0 - status_t getRequestedLuts( - PhysicalDisplayId, - std::vector*) - override; + // get a mapper + HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; @@ -571,6 +569,8 @@ private: const size_t mMaxVirtualDisplayDimension; const bool mUpdateDeviceProductInfoOnHotplugReconnect; bool mEnableVrrTimeout; + + HWC2::Display::LutFileDescriptorMapper mLutFileDescriptorMapper; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index ee1e07ae7d..056ecd78f4 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -1410,7 +1410,8 @@ Error HidlComposer::getClientTargetProperty( return Error::NONE; } -Error HidlComposer::getRequestedLuts(Display, std::vector*) { +Error HidlComposer::getRequestedLuts(Display, std::vector*, + std::vector*) { return Error::NONE; } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index 701a54bc66..1cc23d1409 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -352,7 +352,7 @@ public: Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; Error notifyExpectedPresent(Display, nsecs_t, int32_t) override; Error getRequestedLuts( - Display, + Display, std::vector*, std::vector*) override; Error setLayerLuts(Display, Layer, diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 4bfbde5f28..a50c344de4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1648,6 +1648,22 @@ status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties outProperties->combinations.emplace_back(outCombination); } outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces; + if (aidlProperties.lutProperties.has_value()) { + std::vector outLutProperties; + for (const auto& properties : aidlProperties.lutProperties.value()) { + gui::LutProperties currentProperties; + currentProperties.dimension = + static_cast(properties->dimension); + currentProperties.size = properties->size; + currentProperties.samplingKeys.reserve(properties->samplingKeys.size()); + std::transform(properties->samplingKeys.cbegin(), properties->samplingKeys.cend(), + std::back_inserter(currentProperties.samplingKeys), [](const auto& val) { + return static_cast(val); + }); + outLutProperties.push_back(std::move(currentProperties)); + } + outProperties->lutProperties.emplace(outLutProperties.begin(), outLutProperties.end()); + } return NO_ERROR; } diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index f472d8fefe..615cc948ed 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -182,7 +182,7 @@ public: MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t)); MOCK_METHOD( Error, getRequestedLuts, - (Display, + (Display, std::vector*, std::vector*)); MOCK_METHOD(Error, setLayerLuts, (Display, Layer, std::vector&)); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 5edd2cd9e3..53ed2e1f20 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -111,7 +111,7 @@ public: (aidl::android::hardware::graphics::composer3::OverlayProperties *), (const override)); MOCK_METHOD(hal::Error, getRequestedLuts, - (std::vector*), + ((HWC2::Display::LayerLuts*), (HWC2::Display::LutFileDescriptorMapper&)), (override)); }; -- cgit v1.2.3-59-g8ed1b From 5d59a429f0b0954f8183d4931db8417e308efca8 Mon Sep 17 00:00:00 2001 From: Paul Ramirez Date: Tue, 1 Oct 2024 15:59:16 +0000 Subject: Add file to use new consumer with a subset of TouchResampling_test.cpp This CL adds equivalent tests as those used for InputConsumer to InputConsumerResampling_test.cpp, in order to ensure consistent behavior between the two. Furthermore, a recently developed infrastructure makes these tests single-threaded. Bug: 297226446 Flag: EXEMPT refactor Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="InputConsumerResamplingTest*" Change-Id: I0d065b4368d81a5ea3b7e8791299288f899c6732 --- libs/input/tests/Android.bp | 1 + libs/input/tests/InputConsumerResampling_test.cpp | 434 ++++++++++++++++++++++ 2 files changed, 435 insertions(+) create mode 100644 libs/input/tests/InputConsumerResampling_test.cpp (limited to 'libs') diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 81c6175805..661c9f739f 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -17,6 +17,7 @@ cc_test { "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputConsumer_test.cpp", + "InputConsumerResampling_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp new file mode 100644 index 0000000000..61a0a98c9e --- /dev/null +++ b/libs/input/tests/InputConsumerResampling_test.cpp @@ -0,0 +1,434 @@ + +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include

Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate. + * + *

Set `desiredMinRate` = 0 to indicate the window has no preference + * and any frame rate is acceptable. + * + *

The value should be greater than or equal to 0. + * + * \param desiredMaxRate The desired maximum frame rate (inclusive) for the window, specifying that + * the surface prefers the device render rate to be at most `desiredMaxRate`. + * + *

Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate. + * + *

Set `desiredMaxRate` = positive infinity to indicate the window has no preference + * and any frame rate is acceptable. + * + *

The value should be greater than or equal to `desiredMinRate`. + * + * \param fixedSourceRate The "fixed source" frame rate of the window if the content has an + * inherently fixed frame rate, e.g. a video that has a specific frame rate. + * + *

When the frame rate chosen for the surface is the `fixedSourceRate` or a + * multiple, the surface can render without frame pulldown, for optimal smoothness. For + * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps, + * 60 fps, 90 fps, 120 fps, and so on. + * + *

Setting the fixed source rate can also be used together with a desired + * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still + * means the window's content has a fixed frame rate of `fixedSourceRate`, but additionally + * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an + * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth + * animation on the same window which looks good when drawing within a frame rate range such as + * [`desiredMinRate`, `desiredMaxRate`] = [60,120]. + * + * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface + * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such + * as a black screen for a second or two. + * + * \return 0 for success, -EINVAL if the arguments are invalid. + */ +int32_t ANativeWindow_setFrameRateParams( + ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate, + ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36); + /** * Clears the frame rate which is set for this window. * diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 33c303ae71..05f49ad25f 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1156,6 +1156,23 @@ static inline int native_window_set_frame_rate(struct ANativeWindow* window, flo (int)compatibility, (int)changeFrameRateStrategy); } +static inline int native_window_set_frame_rate_params(struct ANativeWindow* window, + float desiredMinRate, float desiredMaxRate, + float fixedSourceRate, + int8_t changeFrameRateStrategy) { + // TODO(b/362798998): Fix plumbing to send whole params + int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT + : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; + double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE + ? fixedSourceRate + : desiredMinRate; + if (desiredMaxRate < desiredMinRate) { + return -EINVAL; + } + return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, frameRate, compatibility, + changeFrameRateStrategy); +} + struct ANativeWindowFrameTimelineInfo { // Frame Id received from ANativeWindow_getNextFrameId. uint64_t frameNumber; diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index e29d5a6bb4..071e3548d0 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -53,6 +53,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersTransform; ANativeWindow_setDequeueTimeout; # systemapi introduced=30 ANativeWindow_setFrameRate; # introduced=30 + ANativeWindow_setFrameRateParams; # introduced=36 ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk ANativeWindow_setSwapInterval; # llndk -- cgit v1.2.3-59-g8ed1b From 0b3325dc18b4b16f6334144b91a55ce0e8291d29 Mon Sep 17 00:00:00 2001 From: Arpit Singh Date: Fri, 25 Oct 2024 20:34:22 +0000 Subject: Add flag for connected displays cursor Flag to allow cursor to transition across connected displays Bug: 362719483 Test: presubmit FLAG: com.android.input.flags.connected_displays_cursor Change-Id: Ia247af2f323718524e2a76bc9278fe994bb508f7 --- libs/input/input_flags.aconfig | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'libs') diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 484a5df8ca..fd7704815f 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -224,3 +224,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "connected_displays_cursor" + namespace: "lse_desktop_experience" + description: "Allow cursor to transition across multiple connected displays" + bug: "362719483" +} -- cgit v1.2.3-59-g8ed1b From c16a4a5fdef98805af00ee04dcad74622bf3e31b Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Sat, 26 Oct 2024 01:48:01 -0500 Subject: Drain buffer release thread in binder callback Bug: 294133380 Flag: com.android.graphics.libgui.flags.buffer_release_channel Test: BLASTBufferQueueTest Change-Id: Ife6abb1ad9253bda836f846907e9bfba225c3dc9 --- libs/gui/BLASTBufferQueue.cpp | 154 ++++++++------------------------ libs/gui/include/gui/BLASTBufferQueue.h | 15 +--- 2 files changed, 37 insertions(+), 132 deletions(-) (limited to 'libs') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 49f4cba284..fdc39ed765 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -52,19 +52,18 @@ using namespace std::chrono_literals; namespace { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) -// RAII wrapper to defer arbitrary work until the Deferred instance is deleted. -template -class Deferred { +template +class UnlockGuard { public: - explicit Deferred(F f) : mF{std::move(f)} {} + explicit UnlockGuard(Mutex& lock) : mLock{lock} { mLock.unlock(); } - ~Deferred() { mF(); } + ~UnlockGuard() { mLock.lock(); } - Deferred(const Deferred&) = delete; - Deferred& operator=(const Deferred&) = delete; + UnlockGuard(const UnlockGuard&) = delete; + UnlockGuard& operator=(const UnlockGuard&) = delete; private: - F mF; + Mutex& mLock; }; #endif @@ -271,9 +270,6 @@ BLASTBufferQueue::~BLASTBufferQueue() { void BLASTBufferQueue::onFirstRef() { // safe default, most producers are expected to override this mProducer->setMaxDequeuedBufferCount(2); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - mBufferReleaseThread.emplace(sp::fromExisting(this)); -#endif } void BLASTBufferQueue::update(const sp& surface, uint32_t width, uint32_t height, @@ -297,11 +293,16 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, mSurfaceControl = surface; SurfaceComposerClient::Transaction t; if (surfaceControlChanged) { - t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, - layer_state_t::eEnableBackpressure); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer); + // SELinux policy may prevent this process from sending the BufferReleaseChannel's file + // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. This + // transaction is applied separately to ensure we don't lose the other updates. + t.setApplyToken(mApplyToken) + .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer) + .apply(false /* synchronous */, true /* oneWay */); #endif + t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, + layer_state_t::eEnableBackpressure); applyTransaction = true; } mTransformHint = mSurfaceControl->getTransformHint(); @@ -325,7 +326,7 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, } if (applyTransaction) { // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction - t.setApplyToken(mApplyToken).apply(false, true); + t.setApplyToken(mApplyToken).apply(false /* synchronous */, true /* oneWay */); } } @@ -419,7 +420,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp staleReleases; for (const auto& [key, value]: mSubmitted) { @@ -435,7 +435,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const spreleaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + bbq->drainBufferReleaseConsumer(); +#endif }; } @@ -535,8 +537,6 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, const sp& 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--; @@ -646,12 +646,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - ReleaseBufferCallback releaseBufferCallback = - applyTransaction ? nullptr : makeReleaseBufferCallbackThunk(); -#else auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); -#endif sp fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; @@ -1230,12 +1225,7 @@ public: // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. bbq->mBufferReleaseReader->clearInterrupts(); - bbq->mThreadsBlockingOnDequeue++; - bufferQueueLock.unlock(); - Deferred cleanup{[&]() { - bufferQueueLock.lock(); - bbq->mThreadsBlockingOnDequeue--; - }}; + UnlockGuard unlockGuard{bufferQueueLock}; ATRACE_FORMAT("waiting for free buffer"); ReleaseCallbackId id; @@ -1345,6 +1335,21 @@ void BLASTBufferQueue::setApplyToken(sp applyToken) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) +void BLASTBufferQueue::drainBufferReleaseConsumer() { + ATRACE_CALL(); + while (true) { + ReleaseCallbackId id; + sp fence; + uint32_t maxAcquiredBufferCount; + status_t status = + mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount); + if (status != OK) { + return; + } + releaseBufferCallback(id, fence, maxAcquiredBufferCount); + } +} + BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} { mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)}; LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(), @@ -1438,95 +1443,6 @@ void BLASTBufferQueue::BufferReleaseReader::clearInterrupts() { } } -BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp& bbq) { - android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)}; - LOG_ALWAYS_FATAL_IF(!epollFd.ok(), - "Failed to create buffer release background thread epoll file descriptor. " - "errno=%d message='%s'", - errno, strerror(errno)); - - epoll_event registerEndpointFd{}; - registerEndpointFd.events = EPOLLIN; - registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd(); - status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(), - ®isterEndpointFd); - LOG_ALWAYS_FATAL_IF(status == -1, - "Failed to register background thread buffer release consumer file " - "descriptor with epoll. errno=%d message='%s'", - errno, strerror(errno)); - - // EventFd is used to break the background thread's loop. - android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)}; - LOG_ALWAYS_FATAL_IF(!eventFd.ok(), - "Failed to create background thread buffer release event file descriptor. " - "errno=%d message='%s'", - errno, strerror(errno)); - - epoll_event registerEventFd{}; - registerEventFd.events = EPOLLIN; - registerEventFd.data.fd = eventFd.get(); - status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), ®isterEventFd); - LOG_ALWAYS_FATAL_IF(status == -1, - "Failed to register background thread event file descriptor with epoll. " - "errno=%d message='%s'", - errno, strerror(errno)); - - mEventFd = eventFd.get(); - - std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd), - weakBbq = wp(bbq)]() { - pthread_setname_np(pthread_self(), "BufferReleaseThread"); - while (true) { - epoll_event event{}; - int eventCount; - do { - eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/); - } while (eventCount == -1 && errno != EINTR); - - if (eventCount == -1) { - ALOGE("epoll_wait error while waiting for buffer release in background thread. " - "errno=%d message='%s'", - errno, strerror(errno)); - continue; - } - - // EventFd is used to join this thread. - if (event.data.fd == eventFd.get()) { - return; - } - - sp bbq = weakBbq.promote(); - if (!bbq) { - return; - } - - // If there are threads blocking on dequeue, give those threads priority for handling - // the release. - if (bbq->mThreadsBlockingOnDequeue > 0) { - std::this_thread::sleep_for(0ms); - continue; - } - - ReleaseCallbackId id; - sp fence; - uint32_t maxAcquiredBufferCount; - status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence, - maxAcquiredBufferCount); - if (status != OK) { - ALOGE("failed to read from buffer release consumer in background thread. errno=%d " - "message='%s'", - errno, strerror(errno)); - continue; - } - bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount); - } - }).detach(); -} - -BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() { - eventfd_write(mEventFd, 1); -} - #endif } // namespace android diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 99c64daa15..4fd44e52f4 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -325,6 +325,8 @@ private: std::unique_ptr mBufferReleaseConsumer; std::shared_ptr mBufferReleaseProducer; + void drainBufferReleaseConsumer(); + class BufferReleaseReader { public: explicit BufferReleaseReader(BLASTBufferQueue&); @@ -353,19 +355,6 @@ private: }; std::optional mBufferReleaseReader; - - std::atomic mThreadsBlockingOnDequeue = 0; - - class BufferReleaseThread { - public: - BufferReleaseThread(const sp&); - ~BufferReleaseThread(); - - private: - int mEventFd; - }; - - std::optional mBufferReleaseThread; #endif }; -- cgit v1.2.3-59-g8ed1b From bb504471bbefd0fe7d93fd216c12e6cec345b669 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Fri, 25 Oct 2024 13:20:32 -0500 Subject: Clean up BufferReleaseChannel logging Bug: 294133380 Flag: com.android.graphics.libgui.flags.buffer_release_channel Test: BLASTBufferQueueTest Change-Id: Ia5e0299794f57c8741649dafd6c1cc9798559e09 --- libs/gui/BufferReleaseChannel.cpp | 59 ++++++++++------------ services/surfaceflinger/Layer.cpp | 16 +++++- .../surfaceflinger/TransactionCallbackInvoker.cpp | 11 ++-- .../surfaceflinger/TransactionCallbackInvoker.h | 1 + 4 files changed, 50 insertions(+), 37 deletions(-) (limited to 'libs') diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp index e9c6ef3512..eb50684412 100644 --- a/libs/gui/BufferReleaseChannel.cpp +++ b/libs/gui/BufferReleaseChannel.cpp @@ -35,35 +35,35 @@ namespace android::gui { namespace { template -static void readAligned(const void*& buffer, size_t& size, T& value) { +void readAligned(const void*& buffer, size_t& size, T& value) { size -= FlattenableUtils::align(buffer); FlattenableUtils::read(buffer, size, value); } template -static void writeAligned(void*& buffer, size_t& size, T value) { +void writeAligned(void*& buffer, size_t& size, T value) { size -= FlattenableUtils::align(buffer); FlattenableUtils::write(buffer, size, value); } template -static void addAligned(size_t& size, T /* value */) { +void addAligned(size_t& size, T /* value */) { size = FlattenableUtils::align(size); size += sizeof(T); } template -static inline constexpr uint32_t low32(const T n) { +inline constexpr uint32_t low32(const T n) { return static_cast(static_cast(n)); } template -static inline constexpr uint32_t high32(const T n) { +inline constexpr uint32_t high32(const T n) { return static_cast(static_cast(n) >> 32); } template -static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { +inline constexpr T to64(const uint32_t lo, const uint32_t hi) { return static_cast(static_cast(hi) << 32 | lo); } @@ -139,19 +139,18 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( std::lock_guard lock{mMutex}; Message message; mFlattenedBuffer.resize(message.getFlattenedSize()); - std::array controlMessageBuffer; + std::array controlMessageBuffer{}; iovec iov{ .iov_base = mFlattenedBuffer.data(), .iov_len = mFlattenedBuffer.size(), }; - msghdr msg{ - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = controlMessageBuffer.data(), - .msg_controllen = controlMessageBuffer.size(), - }; + msghdr msg{}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = controlMessageBuffer.data(); + msg.msg_controllen = controlMessageBuffer.size(); ssize_t result; do { @@ -161,7 +160,7 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( if (errno == EWOULDBLOCK || errno == EAGAIN) { return WOULD_BLOCK; } - ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno)); + ALOGE("Error reading release fence from socket: error %d (%s)", errno, strerror(errno)); return UNKNOWN_ERROR; } @@ -200,9 +199,9 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( return OK; } -int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId, - const sp& fence, - uint32_t maxAcquiredBufferCount) { +status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence( + const ReleaseCallbackId& callbackId, const sp& fence, + uint32_t maxAcquiredBufferCount) { Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; mFlattenedBuffer.resize(message.getFlattenedSize()); int flattenedFd; @@ -213,25 +212,22 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb size_t flattenedBufferSize = mFlattenedBuffer.size(); int* flattenedFdPtr = &flattenedFd; size_t flattenedFdCount = 1; - if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, - flattenedFdCount); - err != OK) { - ALOGE("Failed to flatten BufferReleaseChannel message."); - return err; + if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize, + flattenedFdPtr, flattenedFdCount); + status != OK) { + return status; } } - iovec iov{ - .iov_base = mFlattenedBuffer.data(), - .iov_len = mFlattenedBuffer.size(), - }; + iovec iov{}; + iov.iov_base = mFlattenedBuffer.data(); + iov.iov_len = mFlattenedBuffer.size(); - msghdr msg{ - .msg_iov = &iov, - .msg_iovlen = 1, - }; + msghdr msg{}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; - std::array controlMessageBuffer; + std::array controlMessageBuffer{}; if (fence && fence->isValid()) { msg.msg_control = controlMessageBuffer.data(); msg.msg_controllen = controlMessageBuffer.size(); @@ -248,7 +244,6 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb result = sendmsg(mFd, &msg, 0); } while (result == -1 && errno == EINTR); if (result == -1) { - ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno)); return -errno; } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index c88092b23f..20ba45f96e 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -689,8 +689,20 @@ void Layer::callReleaseBufferCallback(const sp& l listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount); } - if (mBufferReleaseChannel) { - mBufferReleaseChannel->writeReleaseFence(callbackId, fence, currentMaxAcquiredBufferCount); + if (!mBufferReleaseChannel) { + return; + } + + status_t status = mBufferReleaseChannel->writeReleaseFence(callbackId, fence, + currentMaxAcquiredBufferCount); + if (status != OK) { + int error = -status; + // callReleaseBufferCallback is called during Layer's destructor. In this case, it's + // expected to receive connection errors. + if (error != EPIPE && error != ECONNRESET) { + ALOGD("[%s] writeReleaseFence failed. error %d (%s)", getDebugName(), error, + strerror(error)); + } } } diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index de4825ba62..b22ec66819 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -144,7 +144,7 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp& eventStats, handle->previousReleaseCallbackId); if (handle->bufferReleaseChannel && handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) { - mBufferReleases.emplace_back(handle->bufferReleaseChannel, + mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel, handle->previousReleaseCallbackId, handle->previousReleaseFence, handle->currentMaxAcquiredBufferCount); @@ -159,8 +159,13 @@ void TransactionCallbackInvoker::addPresentFence(sp presentFence) { void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { for (const auto& bufferRelease : mBufferReleases) { - bufferRelease.channel->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence, - bufferRelease.currentMaxAcquiredBufferCount); + status_t status = bufferRelease.channel + ->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence, + bufferRelease.currentMaxAcquiredBufferCount); + if (status != OK) { + ALOGE("[%s] writeReleaseFence failed. error %d (%s)", bufferRelease.layerName.c_str(), + -status, strerror(-status)); + } } mBufferReleases.clear(); diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index d81d8d07f4..178ddbbe79 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -83,6 +83,7 @@ private: mCompletedTransactions; struct BufferRelease { + std::string layerName; std::shared_ptr channel; ReleaseCallbackId callbackId; sp fence; -- cgit v1.2.3-59-g8ed1b From 603c2ea98cf0934f1ad0f9511dae31cfb372e9f2 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Mon, 28 Oct 2024 15:25:47 -0500 Subject: Add unit tests to check that BufferReleaseChannel is unidirectional Bug: 294133380 Flag: com.android.graphics.libgui.flags.buffer_release_channel Test: BufferReleaseChannelTest Change-Id: Iae20dc9e24826fbfd372717d64081a40c7ab1bab --- libs/gui/BufferReleaseChannel.cpp | 7 ----- libs/gui/tests/BufferReleaseChannel_test.cpp | 47 ++++++++++++++++++---------- 2 files changed, 31 insertions(+), 23 deletions(-) (limited to 'libs') diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp index eb50684412..e9cb013baf 100644 --- a/libs/gui/BufferReleaseChannel.cpp +++ b/libs/gui/BufferReleaseChannel.cpp @@ -339,13 +339,6 @@ status_t BufferReleaseChannel::open(std::string name, return -errno; } - // Make the producer write-only - if (shutdown(producerFd.get(), SHUT_RD) == -1) { - ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'", - name.c_str(), errno, strerror(errno)); - return -errno; - } - outConsumer = std::make_unique(name, std::move(consumerFd)); outProducer = std::make_shared(std::move(name), std::move(producerFd)); return STATUS_OK; diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp index 11d122b525..74f69e1ff0 100644 --- a/libs/gui/tests/BufferReleaseChannel_test.cpp +++ b/libs/gui/tests/BufferReleaseChannel_test.cpp @@ -29,11 +29,11 @@ namespace { // Helper function to check if two file descriptors point to the same file. bool is_same_file(int fd1, int fd2) { - struct stat stat1; + struct stat stat1 {}; if (fstat(fd1, &stat1) != 0) { return false; } - struct stat stat2; + struct stat stat2 {}; if (fstat(fd2, &stat2) != 0) { return false; } @@ -42,7 +42,18 @@ bool is_same_file(int fd1, int fd2) { } // namespace -TEST(BufferReleaseChannelTest, MessageFlattenable) { +class BufferReleaseChannelTest : public testing::Test { +protected: + std::unique_ptr mConsumer; + std::shared_ptr mProducer; + + void SetUp() override { + ASSERT_EQ(OK, + BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer)); + } +}; + +TEST_F(BufferReleaseChannelTest, MessageFlattenable) { ReleaseCallbackId releaseCallbackId{1, 2}; sp releaseFence = sp::make(memfd_create("fake-fence-fd", 0)); uint32_t maxAcquiredBufferCount = 5; @@ -92,31 +103,23 @@ TEST(BufferReleaseChannelTest, MessageFlattenable) { // Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message // available. -TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { - std::unique_ptr consumer; - std::shared_ptr producer; - ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); - +TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { ReleaseCallbackId releaseCallbackId; sp releaseFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(WOULD_BLOCK, - consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); + mConsumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); } // Verify that we can write a message to the BufferReleaseChannel producer and read that message // using the BufferReleaseChannel consumer. -TEST(BufferReleaseChannelTest, ProduceAndConsume) { - std::unique_ptr consumer; - std::shared_ptr producer; - ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); - +TEST_F(BufferReleaseChannelTest, ProduceAndConsume) { sp fence = sp::make(memfd_create("fake-fence-fd", 0)); for (uint64_t i = 0; i < 64; i++) { ReleaseCallbackId producerId{i, i + 1}; uint32_t maxAcquiredBufferCount = i + 2; - ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); + ASSERT_EQ(OK, mProducer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); } for (uint64_t i = 0; i < 64; i++) { @@ -127,7 +130,7 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { sp consumerFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(OK, - consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); + mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); ASSERT_EQ(expectedId, consumerId); ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); @@ -135,4 +138,16 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { } } +// Verify that BufferReleaseChannel::ConsumerEndpoint's socket can't be written to. +TEST_F(BufferReleaseChannelTest, ConsumerSocketReadOnly) { + uint64_t data = 0; + ASSERT_EQ(-1, write(mConsumer->getFd().get(), &data, sizeof(uint64_t))); + ASSERT_EQ(errno, EPIPE); +} + +// Verify that BufferReleaseChannel::ProducerEndpoint's socket can't be read from. +TEST_F(BufferReleaseChannelTest, ProducerSocketWriteOnly) { + ASSERT_EQ(0, read(mProducer->getFd().get(), nullptr, sizeof(uint64_t))); +} + } // namespace android \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 56d55bca36e30c2d247e2d1aafd036b4c9231c28 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Mon, 28 Oct 2024 21:00:30 +0000 Subject: Fix flaky uncropped_container_replaces_touchable_region_with_null_crop The test injects an event that its windows should not receive. Since this is an integration test, that event can go to other windows underneath and cause unintended side effects, like changing touch mode. Create a background container layer where the injected touch will go to prevent it from going to other windows that can potentially affect the test. Bug: 376041354 Change-Id: I252a5ca9c7d4bccd90b85f243e4fba5a1c44b8b7 Test: presubmit Flag: TEST_ONLY --- libs/gui/tests/EndToEndNativeInputTest.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'libs') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 7d0b512cb4..0e2712a352 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -1116,6 +1116,8 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ * in its parent's touchable region. The input events should be in the layer's coordinate space. */ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) { + std::unique_ptr bgContainer = + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr parentContainer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr containerSurface = @@ -1124,6 +1126,9 @@ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_nul [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + parentContainer->doTransaction( + [&](auto& t, auto& sc) { t.reparent(sc, bgContainer->mSurfaceControl); }); + bgContainer->showAt(0, 0, Rect(0, 0, 100, 100)); parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect::INVALID_RECT); -- cgit v1.2.3-59-g8ed1b From a97fd945a60da6bc5563bd4fa15e3ffc496bb082 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Mon, 28 Oct 2024 21:21:53 +0000 Subject: EndToEndNativeInputTest: Apply all transactions synchronously Bug: 376016199 Change-Id: Ied11c638f12434e4b1d33e07a24f8b03e6d9492e Test: Presubmit Flag: TEST_ONLY --- libs/gui/tests/EndToEndNativeInputTest.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 7d0b512cb4..a481d12ae4 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -294,7 +294,7 @@ public: transactionBody) { SurfaceComposerClient::Transaction t; transactionBody(t, mSurfaceControl); - t.apply(true); + t.apply(/*synchronously=*/true); } virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { @@ -307,7 +307,7 @@ public: t.setAlpha(mSurfaceControl, 1); auto reportedListener = sp::make(); t.addWindowInfosReportedListener(reportedListener); - t.apply(); + t.apply(/*synchronously=*/true); reportedListener->wait(); } @@ -319,7 +319,7 @@ public: request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId.val(); t.setFocusedWindow(request); - t.apply(true); + t.apply(/*synchronously=*/true); } public: @@ -363,7 +363,7 @@ public: transactionBody) override { SurfaceComposerClient::Transaction t; transactionBody(t, mParentSurfaceControl); - t.apply(true); + t.apply(/*synchronously=*/true); } void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override { @@ -377,7 +377,7 @@ public: t.setInputWindowInfo(mSurfaceControl, mInputInfo); t.setCrop(mSurfaceControl, crop); t.setAlpha(mSurfaceControl, 1); - t.apply(true); + t.apply(/*synchronously=*/true); } private: @@ -417,7 +417,7 @@ public: BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; sp buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test"); - Transaction().setBuffer(layer, buffer).apply(true); + Transaction().setBuffer(layer, buffer).apply(/*synchronously=*/true); usleep(mBufferPostDelay); } @@ -1207,7 +1207,7 @@ public: t.setDisplayLayerStack(token, layerStack); t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height}, {offsetX, offsetY, offsetX + width, offsetY + height}); - t.apply(true); + t.apply(/*synchronously=*/true); mVirtualDisplays.push_back(token); } -- cgit v1.2.3-59-g8ed1b From fb6d585f8c699b0ac98011610f54b4ae9a6b235d Mon Sep 17 00:00:00 2001 From: Harry Cutts Date: Tue, 29 Oct 2024 11:47:32 +0000 Subject: TestEventMatchers: explain WithMotionAction failures Test: $ atest inputflinger_tests (with some code modified to fail with wrong actions or missing FLAG_CANCELED) Test: $ atest libinput_tests Bug: 245989146 Flag: TEST_ONLY Change-Id: Ib0cf2f184167c8d735fdda470f87f9a4ec811be5 --- libs/input/tests/TestEventMatchers.h | 16 ++++++++---- services/inputflinger/tests/TestEventMatchers.h | 33 +++++++++++++++++-------- 2 files changed, 34 insertions(+), 15 deletions(-) (limited to 'libs') diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h index 3589de599f..290a97d736 100644 --- a/libs/input/tests/TestEventMatchers.h +++ b/libs/input/tests/TestEventMatchers.h @@ -75,12 +75,18 @@ public: using is_gtest_matcher = void; explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} - bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { - bool matches = mAction == event.getAction(); - if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const { + if (mAction != event.getAction()) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(event.getAction()); + return false; + } + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL && + (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; } - return matches; + return true; } void DescribeTo(std::ostream* os) const { diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index f58d8fd47a..7078e49343 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -108,20 +108,33 @@ public: using is_gtest_matcher = void; explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} - bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const { - bool matches = mAction == args.action; - if (args.action == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const NotifyMotionArgs& args, + testing::MatchResultListener* listener) const { + if (mAction != args.action) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(args.action); + return false; } - return matches; + if (args.action == AMOTION_EVENT_ACTION_CANCEL && + (args.flags & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; + } + return true; } - bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { - bool matches = mAction == event.getAction(); - if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const { + if (mAction != event.getAction()) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(event.getAction()); + return false; } - return matches; + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL && + (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; + } + return true; } void DescribeTo(std::ostream* os) const { -- cgit v1.2.3-59-g8ed1b From 7e2ba2604fb0e0f5a020f0c3182b87d63f8839b9 Mon Sep 17 00:00:00 2001 From: Harry Cutts Date: Tue, 29 Oct 2024 12:15:29 +0000 Subject: TfLiteMotionPredictor_test: fix initialization order warnings The mismatch in order between the declaration and the initializer lists was causing the following warning: warning: ISO C++ requires field designators to be specified in declaration order; field 'orientation' will be initialized after field 'tilt' [-Wreorder-init-list] Bug: 245989146 Test: atest libinput_tests Flag: TEST_ONLY Change-Id: Ic49c54df27b454c3593064de4778bcde7899f230 --- libs/input/tests/TfLiteMotionPredictor_test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'libs') diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp index c3ac0b7cfb..0c19ebe651 100644 --- a/libs/input/tests/TfLiteMotionPredictor_test.cpp +++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp @@ -89,23 +89,23 @@ TEST(TfLiteMotionPredictorTest, BuffersCopyTo) { buffers.pushSample(/*timestamp=*/1, {.position = {.x = 10, .y = 10}, .pressure = 0, - .orientation = 0, - .tilt = 0.2}); + .tilt = 0.2, + .orientation = 0}); buffers.pushSample(/*timestamp=*/2, {.position = {.x = 10, .y = 50}, .pressure = 0.4, - .orientation = M_PI / 4, - .tilt = 0.3}); + .tilt = 0.3, + .orientation = M_PI / 4}); buffers.pushSample(/*timestamp=*/3, {.position = {.x = 30, .y = 50}, .pressure = 0.5, - .orientation = -M_PI / 4, - .tilt = 0.4}); + .tilt = 0.4, + .orientation = -M_PI / 4}); buffers.pushSample(/*timestamp=*/3, {.position = {.x = 30, .y = 60}, .pressure = 0, - .orientation = 0, - .tilt = 0.5}); + .tilt = 0.5, + .orientation = 0}); buffers.copyTo(*model); const int zeroPadding = model->inputLength() - 3; -- cgit v1.2.3-59-g8ed1b From a419faaaf33e885d614d37aba02b27d2f53a37a9 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Tue, 29 Oct 2024 16:50:27 -0500 Subject: Add warning logs when we fail to set BufferReleaseChannel Bug: 294133380 Flag: com.android.graphics.libgui.flags.buffer_release_channel Test: presubmits Change-Id: I21461ab32ae9c0e75f9cf9851737ed29e4024836 --- libs/gui/BLASTBufferQueue.cpp | 21 +++++++++++++++------ libs/gui/SurfaceComposerClient.cpp | 15 ++++++++------- libs/gui/include/gui/BLASTBufferQueue.h | 6 ++++++ 3 files changed, 29 insertions(+), 13 deletions(-) (limited to 'libs') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index fdc39ed765..495418b921 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -294,12 +294,7 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, SurfaceComposerClient::Transaction t; if (surfaceControlChanged) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - // SELinux policy may prevent this process from sending the BufferReleaseChannel's file - // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. This - // transaction is applied separately to ensure we don't lose the other updates. - t.setApplyToken(mApplyToken) - .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer) - .apply(false /* synchronous */, true /* oneWay */); + updateBufferReleaseProducer(); #endif t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); @@ -1335,6 +1330,20 @@ void BLASTBufferQueue::setApplyToken(sp applyToken) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) +void BLASTBufferQueue::updateBufferReleaseProducer() { + // SELinux policy may prevent this process from sending the BufferReleaseChannel's file + // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. We send this + // transaction independently of any other updates to ensure those updates aren't lost. + SurfaceComposerClient::Transaction t; + status_t status = t.setApplyToken(mApplyToken) + .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer) + .apply(false /* synchronous */, true /* oneWay */); + if (status != OK) { + ALOGW("[%s] %s - failed to set buffer release channel on %s", mName.c_str(), + statusToString(status).c_str(), mSurfaceControl->getName().c_str()); + } +} + void BLASTBufferQueue::drainBufferReleaseConsumer() { ATRACE_CALL(); while (true) { diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a93fc926c2..13e81bd723 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1347,21 +1347,22 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay sp applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken(); sp sf(ComposerService::getComposerService()); - sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken, - mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp, - mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId, - mMergedTransactionIds); + status_t binderStatus = + sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, + applyToken, mInputWindowCommands, mDesiredPresentTime, + mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks, + listenerCallbacks, mId, mMergedTransactionIds); mId = generateId(); // Clear the current states and flags clear(); - if (synchronous) { + if (synchronous && binderStatus == OK) { syncCallback->wait(); } mStatus = NO_ERROR; - return NO_ERROR; + return binderStatus; } sp SurfaceComposerClient::Transaction::sApplyToken = new BBinder(); @@ -1375,7 +1376,7 @@ sp SurfaceComposerClient::Transaction::getDefaultApplyToken() { void SurfaceComposerClient::Transaction::setDefaultApplyToken(sp applyToken) { std::scoped_lock lock{sApplyTokenMutex}; - sApplyToken = applyToken; + sApplyToken = std::move(applyToken); } status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction( diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 4fd44e52f4..8894b66c6d 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -325,8 +325,14 @@ private: std::unique_ptr mBufferReleaseConsumer; std::shared_ptr mBufferReleaseProducer; + void updateBufferReleaseProducer() REQUIRES(mMutex); void drainBufferReleaseConsumer(); + // BufferReleaseReader is used to do blocking but interruptible reads from the buffer + // release channel. To implement this, BufferReleaseReader owns an epoll file descriptor that + // is configured to wake up when either the BufferReleaseReader::ConsumerEndpoint or an eventfd + // becomes readable. Interrupts are necessary because a free buffer may become available for + // reasons other than a buffer release from the producer. class BufferReleaseReader { public: explicit BufferReleaseReader(BLASTBufferQueue&); -- cgit v1.2.3-59-g8ed1b From 783fd3fc1e563b1f287583d38620dfccb9c070d5 Mon Sep 17 00:00:00 2001 From: Michael Krehan Date: Tue, 29 Oct 2024 23:35:52 +0000 Subject: Make tracing_perfetto available to vendor/. Lyric and others will want to access the Perfetto API directly as well. Bug: 373924379 Change-Id: I67379706f91ed8c76e6c26f2c90560c7c447673c Flag: EXEMPT No binary change, just build target visibility changes --- libs/tracing_perfetto/Android.bp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libs') diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp index b5c56c5c52..9a2d4f7463 100644 --- a/libs/tracing_perfetto/Android.bp +++ b/libs/tracing_perfetto/Android.bp @@ -47,4 +47,6 @@ cc_library_shared { ], host_supported: true, + // for vndbinder + vendor_available: true, } -- cgit v1.2.3-59-g8ed1b From a48146490b27d104ba0151da80a3fb92538b22ac Mon Sep 17 00:00:00 2001 From: Nolan Scobie Date: Wed, 30 Oct 2024 15:15:00 -0400 Subject: Note Ganesh vs. Graphite in RenderEngine's dumpsys section Also remove empty lines between dumped Vulkan extensions, which improves overall legibility of the section IMO. Bug: b/293371537 Test: Manual validation of `dumpsys SurfaceFlinger` across backends Flag: EXEMPT log only update Change-Id: I25a7f09d2adbdc2c2859fac154a038485aea2484 --- libs/renderengine/skia/GaneshVkRenderEngine.cpp | 8 ++++++++ libs/renderengine/skia/GaneshVkRenderEngine.h | 1 + libs/renderengine/skia/GraphiteVkRenderEngine.cpp | 8 ++++++++ libs/renderengine/skia/GraphiteVkRenderEngine.h | 1 + libs/renderengine/skia/SkiaGLRenderEngine.cpp | 2 +- libs/renderengine/skia/SkiaVkRenderEngine.cpp | 16 +++++++++------- libs/renderengine/skia/SkiaVkRenderEngine.h | 2 +- 7 files changed, 29 insertions(+), 9 deletions(-) (limited to 'libs') diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp index a3a43e20be..cc73f405a6 100644 --- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp +++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp @@ -21,12 +21,15 @@ #include +#include #include #include #include namespace android::renderengine::skia { +using base::StringAppendF; + std::unique_ptr GaneshVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr engine(new GaneshVkRenderEngine(args)); @@ -111,4 +114,9 @@ base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, return res; } +void GaneshVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { + StringAppendF(&result, "\n ------------RE Vulkan (Ganesh)----------\n"); + SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result); +} + } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h index e6123c21bf..ba17f71201 100644 --- a/libs/renderengine/skia/GaneshVkRenderEngine.h +++ b/libs/renderengine/skia/GaneshVkRenderEngine.h @@ -28,6 +28,7 @@ protected: std::unique_ptr createContext(VulkanInterface& vulkanInterface) override; void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp dstSurface) override; + void appendBackendSpecificInfoToDump(std::string& result) override; private: GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {} diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp index 390ad6efd1..a9332fa4e1 100644 --- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp +++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -33,6 +34,8 @@ namespace android::renderengine::skia { +using base::StringAppendF; + std::unique_ptr GraphiteVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr engine(new GraphiteVkRenderEngine(args)); @@ -139,4 +142,9 @@ base::unique_fd GraphiteVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, return drawFenceFd; } +void GraphiteVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { + StringAppendF(&result, "\n ------------RE Vulkan (Graphite)----------\n"); + SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result); +} + } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.h b/libs/renderengine/skia/GraphiteVkRenderEngine.h index cf24a3b756..33a47f1a7f 100644 --- a/libs/renderengine/skia/GraphiteVkRenderEngine.h +++ b/libs/renderengine/skia/GraphiteVkRenderEngine.h @@ -30,6 +30,7 @@ protected: std::unique_ptr createContext(VulkanInterface& vulkanInterface) override; void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp dstSurface) override; + void appendBackendSpecificInfoToDump(std::string& result) override; private: GraphiteVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {} diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 4ef7d5bccb..ddae9fc78f 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -541,7 +541,7 @@ int SkiaGLRenderEngine::getContextPriority() { void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { const GLExtensions& extensions = GLExtensions::getInstance(); - StringAppendF(&result, "\n ------------RE GLES------------\n"); + StringAppendF(&result, "\n ------------RE GLES (Ganesh)------------\n"); StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 677a2b63b2..177abe6c9f 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -169,24 +169,26 @@ int SkiaVkRenderEngine::getContextPriority() { } void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { - StringAppendF(&result, "\n ------------RE Vulkan----------\n"); - StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized()); - StringAppendF(&result, "\n Vulkan protected device initialized: %d\n", + // Subclasses will prepend a backend-specific name / section header + StringAppendF(&result, "Vulkan device initialized: %d\n", sVulkanInterface.isInitialized()); + StringAppendF(&result, "Vulkan protected device initialized: %d\n", sProtectedContentVulkanInterface.isInitialized()); if (!sVulkanInterface.isInitialized()) { return; } - StringAppendF(&result, "\n Instance extensions:\n"); + StringAppendF(&result, "Instance extensions: [\n"); for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) { - StringAppendF(&result, "\n %s\n", name.c_str()); + StringAppendF(&result, " %s\n", name.c_str()); } + StringAppendF(&result, "]\n"); - StringAppendF(&result, "\n Device extensions:\n"); + StringAppendF(&result, "Device extensions: [\n"); for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) { - StringAppendF(&result, "\n %s\n", name.c_str()); + StringAppendF(&result, " %s\n", name.c_str()); } + StringAppendF(&result, "]\n"); } } // namespace skia diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index d2bb3d53cf..88b04df58d 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -81,7 +81,7 @@ protected: SkiaRenderEngine::Contexts createContexts() override; bool supportsProtectedContentImpl() const override; bool useProtectedContextImpl(GrProtected isProtected) override; - void appendBackendSpecificInfoToDump(std::string& result) override; + virtual void appendBackendSpecificInfoToDump(std::string& result) override; // TODO: b/300533018 - refactor this to be non-static static VulkanInterface& getVulkanInterface(bool protectedContext); -- cgit v1.2.3-59-g8ed1b From 508e64d9fb013b07d6cb4df2ff6c2425acabebfa Mon Sep 17 00:00:00 2001 From: Zimuzo Ezeozue Date: Wed, 30 Oct 2024 18:31:03 +0000 Subject: Fix track event names with atrace via Perfetto The perfettoTraceCounter method was broken because there was no way to emit the equivalent of NamedTracks (non registered tracks) for counters. This was fixed in aosp/3284918 with the introduction of a more general API to create TrackDescriptors. Fixed the counter track with this new method. Additionally, a new atrace_name was added to the track descriptor that doesn't guarantee the strings are fixed (for privacy reason). Utilized the atrace_name for both slice and counter tracks instead of the existing static_name. Bug: 303199244 Test: Manual Flag: android.os.perfetto_sdk_tracing Change-Id: I376dc2f661bf1df4968a2983a1bb0f7dbb018f5c --- .../tracing_perfetto/tracing_perfetto_internal.cpp | 57 ++++++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) (limited to 'libs') diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index 9a0042aee5..c4f866338a 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -253,15 +253,31 @@ void perfettoTraceEnd(const struct PerfettoTeCategory& category) { void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, const char* trackName, uint64_t cookie) { PERFETTO_TE( - category, PERFETTO_TE_SLICE_BEGIN(name), - PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + category, PERFETTO_TE_SLICE_BEGIN(name), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, cookie, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, const char* trackName, uint64_t cookie) { - PERFETTO_TE( - category, PERFETTO_TE_SLICE_END(), - PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + PERFETTO_TE( + category, PERFETTO_TE_SLICE_END(), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, cookie, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, @@ -281,14 +297,35 @@ void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, const char* trackName, const char* name) { PERFETTO_TE( - category, PERFETTO_TE_INSTANT(name), - PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid())); + category, PERFETTO_TE_INSTANT(name), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, 1, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceCounter(const struct PerfettoTeCategory& category, - [[maybe_unused]] const char* name, int64_t value) { - PERFETTO_TE(category, PERFETTO_TE_COUNTER(), - PERFETTO_TE_INT_COUNTER(value)); + const char* name, int64_t value) { + PERFETTO_TE( + category, PERFETTO_TE_COUNTER(), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeCounterTrackUuid(name, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + name), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_BYTES( + perfetto_protos_TrackDescriptor_counter_field_number, + PERFETTO_NULL, 0)), + PERFETTO_TE_INT_COUNTER(value)); } } // namespace internal -- cgit v1.2.3-59-g8ed1b From 0a6dc40104010537d473ac8f613c51545e214ebc Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Fri, 19 Jan 2024 14:33:20 -0800 Subject: A uint64 array wrapper for optimized allocation and copying Bug: 315052795 Bug: 357697495 Test: atest libbattery_test; atest FrameworksCoreTests Flag: EXEMPT bugfix Change-Id: I3c09c438131b3f67ef04436667e589d1d86aff71 --- libs/battery/LongArrayMultiStateCounter.cpp | 144 ++++++++++++++++++------ libs/battery/LongArrayMultiStateCounter.h | 61 +++++++++- libs/battery/LongArrayMultiStateCounterTest.cpp | 28 ++--- libs/battery/MultiStateCounter.h | 91 +++++++-------- libs/battery/MultiStateCounterTest.cpp | 7 +- 5 files changed, 231 insertions(+), 100 deletions(-) (limited to 'libs') diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp index 125cfaffa4..35c40ab7e6 100644 --- a/libs/battery/LongArrayMultiStateCounter.cpp +++ b/libs/battery/LongArrayMultiStateCounter.cpp @@ -21,58 +21,134 @@ namespace android { namespace battery { -template <> -bool LongArrayMultiStateCounter::delta(const std::vector& previousValue, - const std::vector& newValue, - std::vector* 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; +Uint64ArrayRW::Uint64ArrayRW(const Uint64Array ©) : Uint64Array(copy.size()) { + if (mSize != 0 && copy.data() != nullptr) { + mData = new uint64_t[mSize]; + memcpy(mData, copy.data(), mSize * sizeof(uint64_t)); + } else { + mData = nullptr; } +} - bool is_delta_valid = true; - for (int i = size - 1; i >= 0; i--) { - if (newValue[i] >= previousValue[i]) { - (*outValue)[i] = newValue[i] - previousValue[i]; +uint64_t *Uint64ArrayRW::dataRW() { + if (mData == nullptr) { + mData = new uint64_t[mSize]; + memset(mData, 0, mSize * sizeof(uint64_t)); + } + return mData; +} + +Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) { + if (t.size() != mSize) { + delete[] mData; + mSize = t.size(); + mData = nullptr; + } + if (mSize != 0) { + if (t.data() != nullptr) { + mData = new uint64_t[mSize]; + memcpy(mData, t.data(), mSize * sizeof(uint64_t)); } else { - (*outValue)[i] = 0; - is_delta_valid = false; + mData = nullptr; } } - return is_delta_valid; + return *this; +} + +std::ostream &operator<<(std::ostream &os, const Uint64Array &v) { + os << "{"; + const uint64_t *data = v.data(); + if (data != nullptr) { + bool first = true; + for (size_t i = 0; i < v.size(); i++) { + if (!first) { + os << ", "; + } + os << data[i]; + first = false; + } + } + os << "}"; + return os; +} + +// Convenience constructor for tests +Uint64ArrayRW::Uint64ArrayRW(std::initializer_list init) : Uint64Array(init.size()) { + mData = new uint64_t[mSize]; + memcpy(mData, init.begin(), mSize * sizeof(uint64_t)); +} + +// Used in tests only. +bool Uint64Array::operator==(const Uint64Array &other) const { + if (size() != other.size()) { + return false; + } + const uint64_t* thisData = data(); + const uint64_t* thatData = other.data(); + for (size_t i = 0; i < mSize; i++) { + const uint64_t v1 = thisData != nullptr ? thisData[i] : 0; + const uint64_t v2 = thatData != nullptr ? thatData[i] : 0; + if (v1 != v2) { + return false; + } + } + return true; } template <> -void LongArrayMultiStateCounter::add(std::vector* value1, - const std::vector& value2, const uint64_t numerator, - const uint64_t denominator) const { +void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2, + const uint64_t numerator, const uint64_t denominator) const { + const uint64_t* data2 = value2.data(); + if (data2 == nullptr) { + return; + } + + uint64_t* data1 = value1->dataRW(); + size_t size = value2.size(); if (numerator != denominator) { - for (int i = value2.size() - 1; i >= 0; i--) { + for (size_t i = 0; i < size; i++) { // The caller ensures that denominator != 0 - (*value1)[i] += value2[i] * numerator / denominator; + data1[i] += data2[i] * numerator / denominator; } } else { - for (int i = value2.size() - 1; i >= 0; i--) { - (*value1)[i] += value2[i]; + for (size_t i = 0; i < size; i++) { + data1[i] += data2[i]; } } } -template <> -std::string LongArrayMultiStateCounter::valueToString(const std::vector& v) const { - std::stringstream s; - s << "{"; - bool first = true; - for (uint64_t n : v) { - if (!first) { - s << ", "; +template<> +bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue, + const Uint64Array &newValue, Uint64ArrayRW *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; + } + if (outValue->size() != size) { + ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size); + return false; + } + + bool is_delta_valid = true; + const uint64_t *prevData = previousValue.data(); + const uint64_t *newData = newValue.data(); + uint64_t *outData = outValue->dataRW(); + for (size_t i = 0; i < size; i++) { + if (prevData == nullptr) { + if (newData == nullptr) { + outData[i] = 0; + } else { + outData[i] = newData[i]; + } + } else if (newData == nullptr || newData[i] < prevData[i]) { + outData[i] = 0; + is_delta_valid = false; + } else { + outData[i] = newData[i] - prevData[i]; } - s << n; - first = false; } - s << "}"; - return s.str(); + return is_delta_valid; } } // namespace battery diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h index f3439f6a0c..e00c96898e 100644 --- a/libs/battery/LongArrayMultiStateCounter.h +++ b/libs/battery/LongArrayMultiStateCounter.h @@ -23,7 +23,66 @@ namespace android { namespace battery { -typedef MultiStateCounter> LongArrayMultiStateCounter; +/** + * Wrapper for an array of uint64's. + */ +class Uint64Array { + protected: + size_t mSize; + + public: + Uint64Array() : Uint64Array(0) {} + + Uint64Array(size_t size) : mSize(size) {} + + virtual ~Uint64Array() {} + + size_t size() const { return mSize; } + + /** + * Returns the wrapped array. + * + * Nullable! Null should be interpreted the same as an array of zeros + */ + virtual const uint64_t *data() const { return nullptr; } + + friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v); + + // Test API + bool operator==(const Uint64Array &other) const; +}; + +/** + * Mutable version of Uint64Array. + */ +class Uint64ArrayRW: public Uint64Array { + uint64_t* mData; + +public: + Uint64ArrayRW() : Uint64ArrayRW(0) {} + + Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {} + + Uint64ArrayRW(const Uint64Array ©); + + // Need an explicit copy constructor. In the initialization context C++ does not understand that + // a Uint64ArrayRW is a Uint64Array. + Uint64ArrayRW(const Uint64ArrayRW ©) : Uint64ArrayRW((const Uint64Array &) copy) {} + + // Test API + Uint64ArrayRW(std::initializer_list init); + + ~Uint64ArrayRW() override { delete[] mData; } + + const uint64_t *data() const override { return mData; } + + // NonNull. Will initialize the wrapped array if it is null. + uint64_t *dataRW(); + + Uint64ArrayRW &operator=(const Uint64Array &t); +}; + +typedef MultiStateCounter LongArrayMultiStateCounter; } // namespace battery } // namespace android diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp index e4e6b2a49f..1c74e3fd14 100644 --- a/libs/battery/LongArrayMultiStateCounterTest.cpp +++ b/libs/battery/LongArrayMultiStateCounterTest.cpp @@ -24,25 +24,25 @@ namespace battery { class LongArrayMultiStateCounterTest : public testing::Test {}; TEST_F(LongArrayMultiStateCounterTest, stateChange) { - LongArrayMultiStateCounter testCounter(2, std::vector(4)); - testCounter.updateValue(std::vector({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({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({50, 100, 150, 200}), testCounter.getCount(0)); - EXPECT_EQ(std::vector({50, 100, 150, 200}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, accumulation) { - LongArrayMultiStateCounter testCounter(2, std::vector(4)); - testCounter.updateValue(std::vector({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); testCounter.setState(0, 4000); - testCounter.updateValue(std::vector({200, 300, 400, 500}), 8000); + testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000); // The first delta is split 50:50: // 0: {50, 100, 150, 200} @@ -50,16 +50,16 @@ TEST_F(LongArrayMultiStateCounterTest, accumulation) { // The second delta is split 4:1 // 0: {80, 80, 80, 80} // 1: {20, 20, 20, 20} - EXPECT_EQ(std::vector({130, 180, 230, 280}), testCounter.getCount(0)); - EXPECT_EQ(std::vector({70, 120, 170, 220}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, toString) { - LongArrayMultiStateCounter testCounter(2, std::vector(4)); - testCounter.updateValue(std::vector({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({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()); diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h index 04b718698e..fadc4ffd41 100644 --- a/libs/battery/MultiStateCounter.h +++ b/libs/battery/MultiStateCounter.h @@ -35,12 +35,12 @@ namespace battery { typedef uint16_t state_t; -template +template class MultiStateCounter { - uint16_t stateCount; + const uint16_t stateCount; + const V emptyValue; state_t currentState; time_t lastStateChangeTimestamp; - T emptyValue; T lastValue; time_t lastUpdateTimestamp; T deltaValue; @@ -54,7 +54,7 @@ class MultiStateCounter { State* states; public: - MultiStateCounter(uint16_t stateCount, const T& emptyValue); + MultiStateCounter(uint16_t stateCount, const V& emptyValue); virtual ~MultiStateCounter(); @@ -66,35 +66,35 @@ public: * Copies the current state and accumulated times-in-state from the source. Resets * the accumulated value. */ - void copyStatesFrom(const MultiStateCounter& source); + void copyStatesFrom(const MultiStateCounter &source); - void setValue(state_t state, const T& value); + void setValue(state_t state, const V& 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); + const V& updateValue(const V& 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); + void incrementValue(const V& 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 addValue(const V& increment); void reset(); uint16_t getStateCount(); - const T& getCount(state_t state); + const V& getCount(state_t state); std::string toString(); @@ -104,27 +104,25 @@ private: * Returns true iff the combination of previousValue and newValue is valid * (newValue >= prevValue) */ - bool delta(const T& previousValue, const T& newValue, T* outValue) const; + bool delta(const T& previousValue, const V& 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, + void add(T* value1, const V& 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 -MultiStateCounter::MultiStateCounter(uint16_t stateCount, const T& emptyValue) +template +MultiStateCounter::MultiStateCounter(uint16_t stateCount, const V& emptyValue) : stateCount(stateCount), + emptyValue(emptyValue), currentState(0), lastStateChangeTimestamp(-1), - emptyValue(emptyValue), lastValue(emptyValue), lastUpdateTimestamp(-1), deltaValue(emptyValue), @@ -136,13 +134,13 @@ MultiStateCounter::MultiStateCounter(uint16_t stateCount, const T& emptyValue } } -template -MultiStateCounter::~MultiStateCounter() { +template +MultiStateCounter::~MultiStateCounter() { delete[] states; }; -template -void MultiStateCounter::setEnabled(bool enabled, time_t timestamp) { +template +void MultiStateCounter::setEnabled(bool enabled, time_t timestamp) { if (enabled == isEnabled) { return; } @@ -167,8 +165,8 @@ void MultiStateCounter::setEnabled(bool enabled, time_t timestamp) { } } -template -void MultiStateCounter::setState(state_t state, time_t timestamp) { +template +void MultiStateCounter::setState(state_t state, time_t timestamp) { if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) { // If the update arrived out-of-order, just push back the timestamp to // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate @@ -198,8 +196,8 @@ void MultiStateCounter::setState(state_t state, time_t timestamp) { lastStateChangeTimestamp = timestamp; } -template -void MultiStateCounter::copyStatesFrom(const MultiStateCounter& source) { +template +void MultiStateCounter::copyStatesFrom(const MultiStateCounter& source) { if (stateCount != source.stateCount) { ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount); return; @@ -214,14 +212,14 @@ void MultiStateCounter::copyStatesFrom(const MultiStateCounter& source) { lastUpdateTimestamp = source.lastUpdateTimestamp; } -template -void MultiStateCounter::setValue(state_t state, const T& value) { +template +void MultiStateCounter::setValue(state_t state, const V& value) { states[state].counter = value; } -template -const T& MultiStateCounter::updateValue(const T& value, time_t timestamp) { - T* returnValue = &emptyValue; +template +const V& MultiStateCounter::updateValue(const V& value, time_t timestamp) { + const V* 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. @@ -250,8 +248,8 @@ const T& MultiStateCounter::updateValue(const T& value, time_t timestamp) { } } else { std::stringstream str; - str << "updateValue is called with a value " << valueToString(value) - << ", which is lower than the previous value " << valueToString(lastValue) + str << "updateValue is called with a value " << value + << ", which is lower than the previous value " << lastValue << "\n"; ALOGE("%s", str.str().c_str()); @@ -276,23 +274,25 @@ const T& MultiStateCounter::updateValue(const T& value, time_t timestamp) { return *returnValue; } -template -void MultiStateCounter::incrementValue(const T& increment, time_t timestamp) { +template +void MultiStateCounter::incrementValue(const V& increment, time_t timestamp) { +// T newValue; +// newValue = lastValue; // Copy assignment, not initialization. T newValue = lastValue; add(&newValue, increment, 1 /* numerator */, 1 /* denominator */); updateValue(newValue, timestamp); } -template -void MultiStateCounter::addValue(const T& value) { +template +void MultiStateCounter::addValue(const V& value) { if (!isEnabled) { return; } add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */); } -template -void MultiStateCounter::reset() { +template +void MultiStateCounter::reset() { lastStateChangeTimestamp = -1; lastUpdateTimestamp = -1; for (int i = 0; i < stateCount; i++) { @@ -301,25 +301,26 @@ void MultiStateCounter::reset() { } } -template -uint16_t MultiStateCounter::getStateCount() { +template +uint16_t MultiStateCounter::getStateCount() { return stateCount; } -template -const T& MultiStateCounter::getCount(state_t state) { +template +const V& MultiStateCounter::getCount(state_t state) { return states[state].counter; } -template -std::string MultiStateCounter::toString() { +template +std::string MultiStateCounter::toString() { std::stringstream str; +// str << "LAST VALUE: " << valueToString(lastValue); str << "["; for (int i = 0; i < stateCount; i++) { if (i != 0) { str << ", "; } - str << i << ": " << valueToString(states[i].counter); + str << i << ": " << states[i].counter; if (states[i].timeInStateSinceUpdate > 0) { str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate; } diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp index a51a38a6c7..589b7fe4e3 100644 --- a/libs/battery/MultiStateCounterTest.cpp +++ b/libs/battery/MultiStateCounterTest.cpp @@ -21,7 +21,7 @@ namespace android { namespace battery { -typedef MultiStateCounter DoubleMultiStateCounter; +typedef MultiStateCounter DoubleMultiStateCounter; template <> bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue, @@ -41,11 +41,6 @@ void DoubleMultiStateCounter::add(double* value1, const double& value2, const ui } } -template <> -std::string DoubleMultiStateCounter::valueToString(const double& v) const { - return std::to_string(v); -} - class MultiStateCounterTest : public testing::Test {}; TEST_F(MultiStateCounterTest, constructor) { -- cgit v1.2.3-59-g8ed1b From bb561921105876e1be4a82cceb71a475e3fa1adf Mon Sep 17 00:00:00 2001 From: Vadim Caen Date: Tue, 29 Oct 2024 15:57:24 +0100 Subject: Fix condition for waiting next frame mNextFrameNumber is initiallized to 1. If we want to wait for the first frame to be drawn, one would expect to call mSurface.waitForNextFrame(0 /* lastFrame */), but since mNextFrameNumber is always greater then lastFrame, the method never waits. Test: N/A Bug: 376248818 Bug: 376828703 Flag: EXEMPT bug fix Change-Id: I8804479bad8102a441a95951ac27c1cc0711d7b0 --- libs/gui/Surface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 66e7ddd915..e41f9bbf43 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -2735,8 +2735,8 @@ status_t Surface::unlockAndPost() bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) { Mutex::Autolock lock(mMutex); - if (mNextFrameNumber > lastFrame) { - return true; + if (mLastFrameNumber > lastFrame) { + return true; } return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK; } -- cgit v1.2.3-59-g8ed1b From e2ca80bb67a3ba690cfe760255af3dd94b88a18a Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Fri, 1 Nov 2024 18:43:11 -0600 Subject: Add flag for applying picture profiles in SurfaceFlinger Bug: 337330263 Test: build Flag: com.android.graphics.surfaceflinger.flags.apply_picture_profiles_sf Change-Id: Ic6d08f00098e79547148626598ede00b1887df95 --- libs/gui/Android.bp | 4 ++++ services/surfaceflinger/common/FlagManager.cpp | 2 ++ services/surfaceflinger/common/include/common/FlagManager.h | 1 + services/surfaceflinger/surfaceflinger_flags_new.aconfig | 8 ++++++++ 4 files changed, 15 insertions(+) (limited to 'libs') diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 1243b214d3..80e148be9f 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -341,6 +341,10 @@ cc_library_shared { "libgui_aidl_headers", ], + static_libs: [ + "libsurfaceflingerflags", + ], + afdo: true, lto: { diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 57ef4c7ac3..bfa3e6e529 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -117,6 +117,7 @@ void FlagManager::dump(std::string& result) const { /// Trunk stable readonly flags /// DUMP_READ_ONLY_FLAG(adpf_fmq_sf); + DUMP_READ_ONLY_FLAG(apply_picture_profiles_sf); DUMP_READ_ONLY_FLAG(connected_display); DUMP_READ_ONLY_FLAG(enable_small_area_detection); DUMP_READ_ONLY_FLAG(frame_rate_category_mrr); @@ -224,6 +225,7 @@ FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED, /// Trunk stable readonly flags /// FLAG_MANAGER_READ_ONLY_FLAG(adpf_fmq_sf, "") +FLAG_MANAGER_READ_ONLY_FLAG(apply_picture_profiles_sf, "") FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "") FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 7716762685..dea76db38a 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -55,6 +55,7 @@ public: /// Trunk stable readonly flags /// bool adpf_fmq_sf() const; + bool apply_picture_profiles_sf() const; bool connected_display() const; bool frame_rate_category_mrr() const; bool enable_small_area_detection() const; diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index ce334e476f..df8cc17ba8 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -18,6 +18,14 @@ flag { bug: "284324521" } # adpf_gpu_sf +flag { + name: "apply_picture_profiles_sf" + namespace: "tv_os_media" + description: "SurfaceFlinger applies picture profile requests and sends them to Composer HAL" + bug: "337330263" + is_fixed_read_only: true +} # apply_picture_profiles_sf + flag { name: "arr_setframerate_api" namespace: "core_graphics" -- cgit v1.2.3-59-g8ed1b From 31b9016a7a3d6a1ecb6d2a59775ec496810f51f4 Mon Sep 17 00:00:00 2001 From: Vaibhav Devmurari Date: Mon, 4 Nov 2024 20:15:56 +0000 Subject: Correctly convert std::string to rust::String Invalid unicode data can lead to panic when trying to convert std::string to rust::String Test: None Bug: 376989545 Flag: EXEMPT bugfix Change-Id: I384924e9b72b5076abf15bd55b38307bfb773ac4 --- libs/input/KeyboardClassifier.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp index 0c2c7be582..2a83919283 100644 --- a/libs/input/KeyboardClassifier.cpp +++ b/libs/input/KeyboardClassifier.cpp @@ -57,14 +57,14 @@ void KeyboardClassifier::notifyKeyboardChanged(DeviceId deviceId, uint32_t deviceClasses) { if (mRustClassifier) { RustInputDeviceIdentifier rustIdentifier; - rustIdentifier.name = identifier.name; - rustIdentifier.location = identifier.location; - rustIdentifier.unique_id = identifier.uniqueId; + rustIdentifier.name = rust::String::lossy(identifier.name); + rustIdentifier.location = rust::String::lossy(identifier.location); + rustIdentifier.unique_id = rust::String::lossy(identifier.uniqueId); rustIdentifier.bus = identifier.bus; rustIdentifier.vendor = identifier.vendor; rustIdentifier.product = identifier.product; rustIdentifier.version = identifier.version; - rustIdentifier.descriptor = identifier.descriptor; + rustIdentifier.descriptor = rust::String::lossy(identifier.descriptor); android::input::keyboardClassifier::notifyKeyboardChanged(**mRustClassifier, deviceId, rustIdentifier, deviceClasses); } else { -- cgit v1.2.3-59-g8ed1b From c034fbf278afb6e91660754c3d6636a63a6f7bf4 Mon Sep 17 00:00:00 2001 From: Melody Hsu Date: Fri, 25 Oct 2024 22:46:40 +0000 Subject: Support SurfaceControlRegistry logs in native Log state changes in native when a transaction is merged or applied. Bug: b/366484871 Test: adb logcat to check for logs Flag: EXEMPT logging Change-Id: Iaf48b6e68743325019c2ee288b4a4648e7153627 --- libs/gui/SurfaceComposerClient.cpp | 19 +++++++++++++++++++ libs/gui/include/gui/SurfaceComposerClient.h | 3 +++ 2 files changed, 22 insertions(+) (limited to 'libs') diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a93fc926c2..d7726fb83b 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -91,6 +91,7 @@ int64_t generateId() { } constexpr int64_t INVALID_VSYNC = -1; +const constexpr char* LOG_SURFACE_CONTROL_REGISTRY = "SurfaceControlRegistry"; } // namespace @@ -872,6 +873,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel const bool earlyWakeupEnd = parcel->readBool(); const int64_t desiredPresentTime = parcel->readInt64(); const bool isAutoTimestamp = parcel->readBool(); + const bool logCallPoints = parcel->readBool(); FrameTimelineInfo frameTimelineInfo; frameTimelineInfo.readFromParcel(parcel); @@ -999,6 +1001,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const parcel->writeBool(mEarlyWakeupEnd); parcel->writeInt64(mDesiredPresentTime); parcel->writeBool(mIsAutoTimestamp); + parcel->writeBool(mLogCallPoints); mFrameTimelineInfo.writeToParcel(parcel); parcel->writeStrongBinder(mApplyToken); parcel->writeUint32(static_cast(mDisplayStates.size())); @@ -1134,6 +1137,12 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo); + mLogCallPoints |= other.mLogCallPoints; + if (mLogCallPoints) { + ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, + "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId); + } + other.clear(); return *this; } @@ -1153,6 +1162,7 @@ void SurfaceComposerClient::Transaction::clear() { mFrameTimelineInfo = {}; mApplyToken = nullptr; mMergedTransactionIds.clear(); + mLogCallPoints = false; } uint64_t SurfaceComposerClient::Transaction::getId() { @@ -1360,6 +1370,10 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay syncCallback->wait(); } + if (mLogCallPoints) { + ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId); + } + mStatus = NO_ERROR; return NO_ERROR; } @@ -1390,6 +1404,11 @@ status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction t.registerSurfaceControlForCallback(sc); return t.apply(/*sync=*/false, /* oneWay=*/true); } + +void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() { + mLogCallPoints = true; +} + // --------------------------------------------------------------------------- sp SurfaceComposerClient::createVirtualDisplay(const std::string& displayName, diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 5ea0c1619b..e9262b3870 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -437,6 +437,8 @@ public: static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other); // Tracks registered callbacks sp mTransactionCompletedListener = nullptr; + // Prints debug logs when enabled. + bool mLogCallPoints = false; protected: std::unordered_map, ComposerState, IBinderHash> mComposerStates; @@ -809,6 +811,7 @@ public: static void setDefaultApplyToken(sp applyToken); static status_t sendSurfaceFlushJankDataTransaction(const sp& sc); + void enableDebugLogCallPoints(); }; status_t clearLayerFrameStats(const sp& token) const; -- cgit v1.2.3-59-g8ed1b From 08ee19997d0ad4fab38465ef878b666c9fffb203 Mon Sep 17 00:00:00 2001 From: Paul Ramirez Date: Thu, 10 Oct 2024 18:02:15 +0000 Subject: Reimplement Chromium's OneEuroFilter to InputConsumer Reimplemented Chromium's OneEuroFilter usage to InputConsumerNoResampling. There are a few differences between Chromium's work and this CL. We reimplemented One Euro filter an adaptive cutoff frequency low pass made in this implementation as in the Chromium's implementation. The class FilteredResampler filters the output of LegacyResampler using the One Euro filter approach. Here's the link to Chromium's to One Euro filter: https://source.chromium.org/chromium/chromium/src/+/main:ui/base/prediction/one_euro_filter.h Bug: 297226446 Flag: EXEMPT bugfix Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST Change-Id: I0316cb1e81c73b1dc28dc809f55dee3a1cc0ebd2 --- include/input/CoordinateFilter.h | 54 +++++++++++++ include/input/OneEuroFilter.h | 101 ++++++++++++++++++++++++ include/input/Resampler.h | 41 ++++++++++ libs/input/Android.bp | 2 + libs/input/CoordinateFilter.cpp | 31 ++++++++ libs/input/OneEuroFilter.cpp | 79 +++++++++++++++++++ libs/input/Resampler.cpp | 30 +++++++ libs/input/tests/Android.bp | 1 + libs/input/tests/OneEuroFilter_test.cpp | 134 ++++++++++++++++++++++++++++++++ 9 files changed, 473 insertions(+) create mode 100644 include/input/CoordinateFilter.h create mode 100644 include/input/OneEuroFilter.h create mode 100644 libs/input/CoordinateFilter.cpp create mode 100644 libs/input/OneEuroFilter.cpp create mode 100644 libs/input/tests/OneEuroFilter_test.cpp (limited to 'libs') diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h new file mode 100644 index 0000000000..f36472dc8c --- /dev/null +++ b/include/input/CoordinateFilter.h @@ -0,0 +1,54 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +namespace android { + +/** + * Pair of OneEuroFilters that independently filter X and Y coordinates. Both filters share the same + * constructor's parameters. The minimum cutoff frequency is the base cutoff frequency, that is, the + * resulting cutoff frequency in the absence of signal's speed. Likewise, beta is a scaling factor + * of the signal's speed that sets how much the signal's speed contributes to the resulting cutoff + * frequency. The adaptive cutoff frequency criterion is f_c = f_c_min + β|̇x_filtered| + */ +class CoordinateFilter { +public: + explicit CoordinateFilter(float minCutoffFreq, float beta); + + /** + * Filters in place only the AXIS_X and AXIS_Y fields from coords. Each call to filter must + * provide a timestamp strictly greater than the timestamp of the previous call. The first time + * this method is invoked no filtering takes place. Subsequent calls do overwrite `coords` with + * filtered data. + * + * @param timestamp The timestamps at which to filter. It must be greater than the one passed in + * the previous call. + * @param coords Coordinates to be overwritten by the corresponding filtered coordinates. + */ + void filter(std::chrono::duration timestamp, PointerCoords& coords); + +private: + OneEuroFilter mXFilter; + OneEuroFilter mYFilter; +}; + +} // namespace android \ No newline at end of file diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h new file mode 100644 index 0000000000..a0168e4f91 --- /dev/null +++ b/include/input/OneEuroFilter.h @@ -0,0 +1,101 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +namespace android { + +/** + * Low pass filter with adaptive low pass frequency based on the signal's speed. The signal's cutoff + * frequency is determined by f_c = f_c_min + β|̇x_filtered|. Refer to + * https://dl.acm.org/doi/10.1145/2207676.2208639 for details on how the filter works and how to + * tune it. + */ +class OneEuroFilter { +public: + /** + * Default cutoff frequency of the filtered signal's speed. 1.0 Hz is the value in the filter's + * paper. + */ + static constexpr float kDefaultSpeedCutoffFreq = 1.0; + + OneEuroFilter() = delete; + + explicit OneEuroFilter(float minCutoffFreq, float beta, + float speedCutoffFreq = kDefaultSpeedCutoffFreq); + + OneEuroFilter(const OneEuroFilter&) = delete; + OneEuroFilter& operator=(const OneEuroFilter&) = delete; + OneEuroFilter(OneEuroFilter&&) = delete; + OneEuroFilter& operator=(OneEuroFilter&&) = delete; + + /** + * Returns the filtered value of rawPosition. Each call to filter must provide a timestamp + * strictly greater than the timestamp of the previous call. The first time the method is + * called, it returns the value of rawPosition. Any subsequent calls provide a filtered value. + * + * @param timestamp The timestamp at which to filter. It must be strictly greater than the one + * provided in the previous call. + * @param rawPosition Position to be filtered. + */ + float filter(std::chrono::duration timestamp, float rawPosition); + +private: + /** + * Minimum cutoff frequency. This is the constant term in the adaptive cutoff frequency + * criterion. Units are Hertz. + */ + const float mMinCutoffFreq; + + /** + * Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the + * filtered signal's speed. The data member is dimensionless, that is, it does not have units. + */ + const float mBeta; + + /** + * Cutoff frequency of the signal's speed. This is the cutoff frequency applied to the filtering + * of the signal's speed. Units are Hertz. + */ + const float mSpeedCutoffFreq; + + /** + * The timestamp from the previous call. Units are seconds. + */ + std::optional> mPrevTimestamp; + + /** + * The raw position from the previous call. + */ + std::optional mPrevRawPosition; + + /** + * The filtered velocity from the previous call. Units are position per second. + */ + std::optional mPrevFilteredVelocity; + + /** + * The filtered position from the previous call. + */ + std::optional mPrevFilteredPosition; +}; + +} // namespace android diff --git a/include/input/Resampler.h b/include/input/Resampler.h index 6d95ca7e86..155097732c 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -19,11 +19,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -293,4 +295,43 @@ private: inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); }; +/** + * Resampler that first applies the LegacyResampler resampling algorithm, then independently filters + * the X and Y coordinates with a pair of One Euro filters. + */ +class FilteredLegacyResampler final : public Resampler { +public: + /** + * Creates a resampler, using the given minCutoffFreq and beta to instantiate its One Euro + * filters. + */ + explicit FilteredLegacyResampler(float minCutoffFreq, float beta); + + void resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, MotionEvent& motionEvent, + const InputMessage* futureMessage) override; + + std::chrono::nanoseconds getResampleLatency() const override; + +private: + LegacyResampler mResampler; + + /** + * Minimum cutoff frequency of the value's low pass filter. Refer to OneEuroFilter class for a + * more detailed explanation. + */ + const float mMinCutoffFreq; + + /** + * Scaling factor of the adaptive cutoff frequency criterion. Refer to OneEuroFilter class for a + * more detailed explanation. + */ + const float mBeta; + + /* + * Note: an associative array with constant insertion and lookup times would be more efficient. + * When this was implemented, there was no container with these properties. + */ + std::map mFilteredPointers; +}; + } // namespace android diff --git a/libs/input/Android.bp b/libs/input/Android.bp index e4e81adf58..a4ae54b351 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -217,6 +217,7 @@ cc_library { ], srcs: [ "AccelerationCurve.cpp", + "CoordinateFilter.cpp", "Input.cpp", "InputConsumer.cpp", "InputConsumerNoResampling.cpp", @@ -230,6 +231,7 @@ cc_library { "KeyLayoutMap.cpp", "MotionPredictor.cpp", "MotionPredictorMetricsManager.cpp", + "OneEuroFilter.cpp", "PrintTools.cpp", "PropertyMap.cpp", "Resampler.cpp", diff --git a/libs/input/CoordinateFilter.cpp b/libs/input/CoordinateFilter.cpp new file mode 100644 index 0000000000..d231474577 --- /dev/null +++ b/libs/input/CoordinateFilter.cpp @@ -0,0 +1,31 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "CoordinateFilter" + +#include + +namespace android { + +CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta) + : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {} + +void CoordinateFilter::filter(std::chrono::duration timestamp, PointerCoords& coords) { + coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX())); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY())); +} + +} // namespace android diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp new file mode 100644 index 0000000000..400d7c9ab0 --- /dev/null +++ b/libs/input/OneEuroFilter.cpp @@ -0,0 +1,79 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "OneEuroFilter" + +#include +#include + +#include +#include + +namespace android { +namespace { + +inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) { + return minCutoffFreq + beta * std::abs(filteredSpeed); +} + +inline float smoothingFactor(std::chrono::duration samplingPeriod, float cutoffFreq) { + return samplingPeriod.count() / (samplingPeriod.count() + (1.0 / (2.0 * M_PI * cutoffFreq))); +} + +inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) { + return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition; +} + +} // namespace + +OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq) + : mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {} + +float OneEuroFilter::filter(std::chrono::duration timestamp, float rawPosition) { + LOG_IF(FATAL, mPrevFilteredPosition.has_value() && (timestamp <= *mPrevTimestamp)) + << "Timestamp must be greater than mPrevTimestamp"; + + const std::chrono::duration samplingPeriod = (mPrevTimestamp.has_value()) + ? (timestamp - *mPrevTimestamp) + : std::chrono::duration{1.0}; + + const float rawVelocity = (mPrevFilteredPosition.has_value()) + ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count()) + : 0.0; + + const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq); + + const float filteredVelocity = (mPrevFilteredVelocity.has_value()) + ? lowPassFilter(rawVelocity, *mPrevFilteredVelocity, speedSmoothingFactor) + : rawVelocity; + + const float positionCutoffFreq = cutoffFreq(mMinCutoffFreq, mBeta, filteredVelocity); + + const float positionSmoothingFactor = smoothingFactor(samplingPeriod, positionCutoffFreq); + + const float filteredPosition = (mPrevFilteredPosition.has_value()) + ? lowPassFilter(rawPosition, *mPrevFilteredPosition, positionSmoothingFactor) + : rawPosition; + + mPrevTimestamp = timestamp; + mPrevRawPosition = rawPosition; + mPrevFilteredVelocity = filteredVelocity; + mPrevFilteredPosition = filteredPosition; + + return filteredPosition; +} + +} // namespace android diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp index 056db093d1..3ab132d550 100644 --- a/libs/input/Resampler.cpp +++ b/libs/input/Resampler.cpp @@ -389,4 +389,34 @@ void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& mo mLastRealSample = *(mLatestSamples.end() - 1); } +// --- FilteredLegacyResampler --- + +FilteredLegacyResampler::FilteredLegacyResampler(float minCutoffFreq, float beta) + : mResampler{}, mMinCutoffFreq{minCutoffFreq}, mBeta{beta} {} + +void FilteredLegacyResampler::resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, + MotionEvent& motionEvent, + const InputMessage* futureSample) { + mResampler.resampleMotionEvent(requestedFrameTime, motionEvent, futureSample); + const size_t numSamples = motionEvent.getHistorySize() + 1; + for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) { + for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); + ++pointerIndex) { + const int32_t pointerId = motionEvent.getPointerProperties(pointerIndex)->id; + const nanoseconds eventTime = + nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}; + // Refer to the static function `setMotionEventPointerCoords` for a justification of + // casting away const. + PointerCoords& pointerCoords = const_cast( + *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex))); + const auto& [iter, _] = mFilteredPointers.try_emplace(pointerId, mMinCutoffFreq, mBeta); + iter->second.filter(eventTime, pointerCoords); + } + } +} + +std::chrono::nanoseconds FilteredLegacyResampler::getResampleLatency() const { + return mResampler.getResampleLatency(); +} + } // namespace android diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 661c9f739f..46e819061f 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -25,6 +25,7 @@ cc_test { "InputVerifier_test.cpp", "MotionPredictor_test.cpp", "MotionPredictorMetricsManager_test.cpp", + "OneEuroFilter_test.cpp", "Resampler_test.cpp", "RingBuffer_test.cpp", "TestInputChannel.cpp", diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp new file mode 100644 index 0000000000..270e789c84 --- /dev/null +++ b/libs/input/tests/OneEuroFilter_test.cpp @@ -0,0 +1,134 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +namespace android { +namespace { + +using namespace std::literals::chrono_literals; +using std::chrono::duration; + +struct Sample { + duration timestamp{}; + double value{}; + + friend bool operator<(const Sample& lhs, const Sample& rhs) { return lhs.value < rhs.value; } +}; + +/** + * Generates a sinusoidal signal with the passed frequency and amplitude. + */ +std::vector generateSinusoidalSignal(duration signalDuration, + double samplingFrequency, double signalFrequency, + double amplitude) { + std::vector signal; + const duration samplingPeriod{1.0 / samplingFrequency}; + for (duration timestamp{0.0}; timestamp < signalDuration; timestamp += samplingPeriod) { + signal.push_back( + Sample{timestamp, + amplitude * std::sin(2.0 * M_PI * signalFrequency * timestamp.count())}); + } + return signal; +} + +double meanAbsoluteError(const std::vector& filteredSignal, + const std::vector& signal) { + if (filteredSignal.size() != signal.size()) { + ADD_FAILURE() << "filteredSignal and signal do not have equal number of samples"; + return std::numeric_limits::max(); + } + std::vector absoluteError; + for (size_t sampleIndex = 0; sampleIndex < signal.size(); ++sampleIndex) { + absoluteError.push_back( + std::abs(filteredSignal[sampleIndex].value - signal[sampleIndex].value)); + } + if (absoluteError.empty()) { + ADD_FAILURE() << "Zero division. absoluteError is empty"; + return std::numeric_limits::max(); + } + return std::accumulate(absoluteError.begin(), absoluteError.end(), 0.0) / absoluteError.size(); +} + +double maxAbsoluteAmplitude(const std::vector& signal) { + if (signal.empty()) { + ADD_FAILURE() << "Max absolute value amplitude does not exist. Signal is empty"; + return std::numeric_limits::max(); + } + std::vector absoluteSignal; + for (const Sample& sample : signal) { + absoluteSignal.push_back(Sample{sample.timestamp, std::abs(sample.value)}); + } + return std::max_element(absoluteSignal.begin(), absoluteSignal.end())->value; +} + +} // namespace + +class OneEuroFilterTest : public ::testing::Test { +protected: + // The constructor's parameters are the ones that Chromium's using. The tuning was based on a 60 + // Hz sampling frequency. Refer to their one_euro_filter.h header for additional information + // about these parameters. + OneEuroFilterTest() : mFilter{/*minCutoffFreq=*/4.7, /*beta=*/0.01} {} + + std::vector filterSignal(const std::vector& signal) { + std::vector filteredSignal; + for (const Sample& sample : signal) { + filteredSignal.push_back( + Sample{sample.timestamp, mFilter.filter(sample.timestamp, sample.value)}); + } + return filteredSignal; + } + + OneEuroFilter mFilter; +}; + +TEST_F(OneEuroFilterTest, PassLowFrequencySignal) { + const std::vector signal = + generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/1, + /*amplitude=*/1); + + const std::vector filteredSignal = filterSignal(signal); + + // The reason behind using the mean absolute error as a metric is that, ideally, a low frequency + // filtered signal is expected to be almost identical to the raw one. Therefore, the error + // between them should be minimal. The constant is heuristically chosen. + EXPECT_LT(meanAbsoluteError(filteredSignal, signal), 0.25); +} + +TEST_F(OneEuroFilterTest, RejectHighFrequencySignal) { + const std::vector signal = + generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/22.5, + /*amplitude=*/1); + + const std::vector filteredSignal = filterSignal(signal); + + // The filtered signal should consist of values that are much closer to zero. The comparison + // constant is heuristically chosen. + EXPECT_LT(maxAbsoluteAmplitude(filteredSignal), 0.25); +} + +} // namespace android -- cgit v1.2.3-59-g8ed1b From f4183a51c4ce2ec89b589215ce55d7907fe4b632 Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Wed, 6 Nov 2024 12:54:24 -0800 Subject: Fix native memory leak in Uint64ArrayRW Bug: 377547685 Test: adb shell dumpsys batterystats --usage; adb shell dumpsys -t 60 meminfo --unreachable `adb shell pidof system_server` | grep "unreachable allocations" Flag: EXEMPT bugfix Change-Id: I2287379427a8de732eda866dfcc3d4129dfb890d --- libs/battery/LongArrayMultiStateCounter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp index 35c40ab7e6..334d84b6b5 100644 --- a/libs/battery/LongArrayMultiStateCounter.cpp +++ b/libs/battery/LongArrayMultiStateCounter.cpp @@ -46,9 +46,12 @@ Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) { } if (mSize != 0) { if (t.data() != nullptr) { - mData = new uint64_t[mSize]; + if (mData == nullptr) { + mData = new uint64_t[mSize]; + } memcpy(mData, t.data(), mSize * sizeof(uint64_t)); } else { + delete[] mData; mData = nullptr; } } -- cgit v1.2.3-59-g8ed1b From 3a707e05da55bf606182f47079159ebe57829420 Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Wed, 6 Nov 2024 15:10:06 -0700 Subject: Move picture profile flag from surfaceflinger to libgui Bug: 337330263 Test: build Flag: com.android.graphics.libgui.flags.apply_picture_profiles Change-Id: I2c1aeba638ed9afc1a7059718d8e20ce86c5db61 --- libs/gui/libgui_flags.aconfig | 8 ++++++++ services/surfaceflinger/common/FlagManager.cpp | 2 -- services/surfaceflinger/common/include/common/FlagManager.h | 1 - services/surfaceflinger/surfaceflinger_flags_new.aconfig | 8 -------- 4 files changed, 8 insertions(+), 11 deletions(-) (limited to 'libs') diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 1c7e0e439c..22d32e9769 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -1,6 +1,14 @@ package: "com.android.graphics.libgui.flags" container: "system" +flag { + name: "apply_picture_profiles" + namespace: "tv_os_media" + description: "This flag controls sending picture profiles from BBQ to Composer HAL" + bug: "337330263" + is_fixed_read_only: true +} # apply_picture_profiles + flag { name: "bq_setframerate" namespace: "core_graphics" diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index bfa3e6e529..57ef4c7ac3 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -117,7 +117,6 @@ void FlagManager::dump(std::string& result) const { /// Trunk stable readonly flags /// DUMP_READ_ONLY_FLAG(adpf_fmq_sf); - DUMP_READ_ONLY_FLAG(apply_picture_profiles_sf); DUMP_READ_ONLY_FLAG(connected_display); DUMP_READ_ONLY_FLAG(enable_small_area_detection); DUMP_READ_ONLY_FLAG(frame_rate_category_mrr); @@ -225,7 +224,6 @@ FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED, /// Trunk stable readonly flags /// FLAG_MANAGER_READ_ONLY_FLAG(adpf_fmq_sf, "") -FLAG_MANAGER_READ_ONLY_FLAG(apply_picture_profiles_sf, "") FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "") FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index dea76db38a..7716762685 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -55,7 +55,6 @@ public: /// Trunk stable readonly flags /// bool adpf_fmq_sf() const; - bool apply_picture_profiles_sf() const; bool connected_display() const; bool frame_rate_category_mrr() const; bool enable_small_area_detection() const; diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index df8cc17ba8..ce334e476f 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -18,14 +18,6 @@ flag { bug: "284324521" } # adpf_gpu_sf -flag { - name: "apply_picture_profiles_sf" - namespace: "tv_os_media" - description: "SurfaceFlinger applies picture profile requests and sends them to Composer HAL" - bug: "337330263" - is_fixed_read_only: true -} # apply_picture_profiles_sf - flag { name: "arr_setframerate_api" namespace: "core_graphics" -- cgit v1.2.3-59-g8ed1b From 7a4cb7e128ccaa6211abb83b510b6ff126b253b5 Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Wed, 30 Oct 2024 10:42:21 -0600 Subject: Add plumbing to pass picture profiles down to Composer HAL Bug: 337330263 Test: atest OutputLayerWriteStateToHWCTest Test: atest OutputUpdateAndWriteCompositionStateTest Flag: com.android.graphics.libgui.flags.apply_picture_profiles Change-Id: I082f4bc47c2d402e15fc3a3de5224889752272fa --- libs/ui/Android.bp | 1 + libs/ui/PictureProfileHandle.cpp | 29 +++++++++++ libs/ui/include/ui/PictureProfileHandle.h | 60 ++++++++++++++++++++++ services/surfaceflinger/DisplayDevice.h | 2 + .../DisplayHardware/AidlComposerHal.cpp | 38 +++++++++++++- .../DisplayHardware/AidlComposerHal.h | 3 ++ .../surfaceflinger/DisplayHardware/ComposerHal.h | 6 ++- services/surfaceflinger/DisplayHardware/HWC2.cpp | 21 ++++++++ services/surfaceflinger/DisplayHardware/HWC2.h | 10 ++++ .../surfaceflinger/DisplayHardware/HWComposer.cpp | 18 +++++++ .../surfaceflinger/DisplayHardware/HWComposer.h | 13 +++-- .../DisplayHardware/HidlComposerHal.cpp | 12 +++++ .../DisplayHardware/HidlComposerHal.h | 3 ++ .../unittests/mock/DisplayHardware/MockComposer.h | 3 ++ .../unittests/mock/DisplayHardware/MockHWC2.h | 8 ++- .../mock/DisplayHardware/MockHWComposer.h | 3 ++ 16 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 libs/ui/PictureProfileHandle.cpp create mode 100644 libs/ui/include/ui/PictureProfileHandle.h (limited to 'libs') diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 12230f99d2..87e213e394 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -136,6 +136,7 @@ cc_library_shared { "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", + "PictureProfileHandle.cpp", "PixelFormat.cpp", "PublicFormat.cpp", "StaticAsserts.cpp", diff --git a/libs/ui/PictureProfileHandle.cpp b/libs/ui/PictureProfileHandle.cpp new file mode 100644 index 0000000000..0701e906f0 --- /dev/null +++ b/libs/ui/PictureProfileHandle.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2009 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 + +#include + +namespace android { + +const PictureProfileHandle PictureProfileHandle::NONE(0); + +::std::string toString(const PictureProfileHandle& handle) { + return std::format("{:#010x}", handle.getId()); +} + +} // namespace android diff --git a/libs/ui/include/ui/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h new file mode 100644 index 0000000000..9b709b6155 --- /dev/null +++ b/libs/ui/include/ui/PictureProfileHandle.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace android { + +/** + * An opaque value that uniquely identifies a picture profile, or a set of parameters, which + * describes the configuration of a picture processing pipeline that is applied to a graphic buffer + * to enhance its quality prior to rendering on the display. + */ +typedef int64_t PictureProfileId; + +/** + * A picture profile handle wraps the picture profile ID for type-safety, and represents an opaque + * handle that doesn't have the performance drawbacks of Binders. + */ +class PictureProfileHandle { +public: + // A profile that represents no picture processing. + static const PictureProfileHandle NONE; + + PictureProfileHandle() { *this = NONE; } + PictureProfileHandle(PictureProfileId id) : mId(id) {} + + PictureProfileId const& getId() const { return mId; } + + inline bool operator==(const PictureProfileHandle& rhs) { return mId == rhs.mId; } + inline bool operator!=(const PictureProfileHandle& rhs) { return !(*this == rhs); } + + // Is the picture profile effectively null, or not-specified? + inline bool operator!() const { return mId == NONE.mId; } + + operator bool() const { return !!*this; } + + friend ::std::string toString(const PictureProfileHandle& handle); + +private: + PictureProfileId mId; +}; + +} // namespace android diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index d09a6b369b..a674593918 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -286,6 +286,8 @@ struct DisplayDeviceState { bool isProtected = false; // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only Fps requestedRefreshRate; + int32_t maxLayerPictureProfiles = 0; + bool hasPictureProcessing = false; private: static std::atomic sNextSequenceId; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 5814aa4354..4c8ff58530 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -44,12 +44,11 @@ using hardware::Return; using aidl::android::hardware::graphics::composer3::BnComposerCallback; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; +using aidl::android::hardware::graphics::composer3::CommandResultPayload; using aidl::android::hardware::graphics::composer3::Luts; using aidl::android::hardware::graphics::composer3::PowerMode; using aidl::android::hardware::graphics::composer3::VirtualDisplay; -using aidl::android::hardware::graphics::composer3::CommandResultPayload; - using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode; using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType; using AidlDisplayIdentification = @@ -1639,6 +1638,41 @@ Error AidlComposer::getPhysicalDisplayOrientation(Display displayId, return Error::NONE; } +Error AidlComposer::getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) { + const auto status = mAidlComposerClient->getMaxLayerPictureProfiles(translate(display), + outMaxProfiles); + if (!status.isOk()) { + ALOGE("getMaxLayerPictureProfiles failed %s", status.getDescription().c_str()); + return static_cast(status.getServiceSpecificError()); + } + return Error::NONE; +} + +Error AidlComposer::setDisplayPictureProfileId(Display display, PictureProfileId id) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setDisplayPictureProfileId(translate(display), id); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + +Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPictureProfileId(translate(display), + translate(layer), id); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + ftl::Optional> AidlComposer::getWriter(Display display) REQUIRES_SHARED(mMutex) { return mWriters.get(display); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index d724b218c0..933e8d186f 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -250,6 +250,9 @@ public: std::vector* outLuts) override; Error setLayerLuts(Display display, Layer layer, Luts& luts) override; + Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; + Error setDisplayPictureProfileId(Display, PictureProfileId id) override; + Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 42ddcd18c8..c1333c29b5 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -29,11 +29,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -44,7 +46,6 @@ #include #include -#include #include // TODO(b/129481165): remove the #pragma below and fix conversion issues @@ -307,6 +308,9 @@ public: virtual Error getRequestedLuts(Display display, std::vector* outLayers, std::vector* outLuts) = 0; virtual Error setLayerLuts(Display display, Layer layer, V3_0::Luts& luts) = 0; + virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0; + virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0; + virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 5355a12cda..a274995eec 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -53,6 +54,7 @@ using android::FloatRect; using android::GraphicBuffer; using android::HdrCapabilities; using android::HdrMetadata; +using android::PictureProfileHandle; using android::Rect; using android::Region; using android::sp; @@ -655,6 +657,16 @@ Error Display::setIdleTimerEnabled(std::chrono::milliseconds timeout) { return static_cast(error); } +Error Display::getMaxLayerPictureProfiles(int32_t* outMaxProfiles) { + const auto error = mComposer.getMaxLayerPictureProfiles(mId, outMaxProfiles); + return static_cast(error); +} + +Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) { + const auto error = mComposer.setDisplayPictureProfileId(mId, handle.getId()); + return static_cast(error); +} + // For use by Device void Display::setConnected(bool connected) { @@ -1086,6 +1098,15 @@ Error Layer::setLuts(aidl::android::hardware::graphics::composer3::Luts& luts) { return static_cast(intError); } +Error Layer::setPictureProfileHandle(const PictureProfileHandle& handle) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + const auto intError = + mComposer.setLayerPictureProfileId(mDisplay->getId(), mId, handle.getId()); + return static_cast(intError); +} + } // namespace impl } // namespace HWC2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 799fd02586..6740d8a832 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -199,6 +200,9 @@ public: [[nodiscard]] virtual hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) = 0; [[nodiscard]] virtual hal::Error getPhysicalDisplayOrientation( Hwc2::AidlTransform* outTransform) const = 0; + [[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0; + [[nodiscard]] virtual hal::Error setPictureProfileHandle( + const PictureProfileHandle& handle) = 0; }; namespace impl { @@ -282,6 +286,8 @@ public: std::optional* support) override; hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override; + hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override; + hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override; // Other Display methods hal::HWDisplayId getId() const override { return mId; } @@ -377,6 +383,8 @@ public: [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0; [[nodiscard]] virtual hal::Error setLuts( aidl::android::hardware::graphics::composer3::Luts& luts) = 0; + [[nodiscard]] virtual hal::Error setPictureProfileHandle( + const PictureProfileHandle& handle) = 0; }; namespace impl { @@ -428,6 +436,7 @@ public: hal::Error setBrightness(float brightness) override; hal::Error setBlockingRegion(const android::Region& region) override; hal::Error setLuts(aidl::android::hardware::graphics::composer3::Luts&) override; + hal::Error setPictureProfileHandle(const PictureProfileHandle& handle) override; private: // These are references to data owned by HWComposer, which will outlive @@ -449,6 +458,7 @@ private: android::HdrMetadata mHdrMetadata; android::mat4 mColorMatrix; uint32_t mBufferSlot; + android::PictureProfileHandle profile; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 7d77634722..61d4541e77 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -1022,6 +1022,24 @@ status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentTyp return NO_ERROR; } +int32_t HWComposer::getMaxLayerPictureProfiles(PhysicalDisplayId displayId) { + int32_t maxProfiles = 0; + RETURN_IF_INVALID_DISPLAY(displayId, 0); + const auto error = mDisplayData[displayId].hwcDisplay->getMaxLayerPictureProfiles(&maxProfiles); + RETURN_IF_HWC_ERROR(error, displayId, 0); + return maxProfiles; +} + +status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId, + const PictureProfileHandle& handle) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = mDisplayData[displayId].hwcDisplay->setPictureProfileHandle(handle); + if (error != hal::Error::UNSUPPORTED) { + RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); + } + return NO_ERROR; +} + const std::unordered_map& HWComposer::getSupportedLayerGenericMetadata() const { return mSupportedLayerGenericMetadata; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 7b04d6755a..e21ce1d095 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -29,6 +29,7 @@ #include #include #include +#include // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -65,6 +66,7 @@ class GraphicBuffer; class TestableSurfaceFlinger; struct HWComposerTest; struct CompositionInfo; +class PictureProfileHandle; namespace Hwc2 { class Composer; @@ -296,7 +298,7 @@ public: virtual std::optional toPhysicalDisplayId(hal::HWDisplayId) const = 0; virtual std::optional fromPhysicalDisplayId(PhysicalDisplayId) const = 0; - // Composer 3.0 + // AIDL Composer virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0; virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0; virtual std::optional getPreferredBootDisplayMode(PhysicalDisplayId) = 0; @@ -315,8 +317,10 @@ public: virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) = 0; - // mapper virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0; + virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0; + virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId, + const PictureProfileHandle& handle) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -480,9 +484,10 @@ public: status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) override; - - // get a mapper HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override; + int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override; + status_t setDisplayPictureProfileHandle(PhysicalDisplayId, + const android::PictureProfileHandle& profile) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 6a7a09b5ae..e359a26f16 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -1446,6 +1446,18 @@ Error HidlComposer::getPhysicalDisplayOrientation(Display, AidlTransform*) { "OptionalFeature::PhysicalDisplayOrientation is not supported on HIDL"); } +Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) { + return Error::UNSUPPORTED; +} + +Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) { + return Error::UNSUPPORTED; +} + +Error HidlComposer::setLayerPictureProfileId(Display, Layer, PictureProfileId) { + return Error::UNSUPPORTED; +} + void HidlComposer::registerCallback(ComposerCallback& callback) { const bool vsyncSwitchingSupported = isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching); diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index a3d1f7f291..9a89dbaa9e 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -357,6 +357,9 @@ public: override; Error setLayerLuts(Display, Layer, aidl::android::hardware::graphics::composer3::Luts&) override; + Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; + Error setDisplayPictureProfileId(Display, PictureProfileId) override; + Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 3e6a768db8..88052db7e2 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -186,6 +186,9 @@ public: std::vector*)); MOCK_METHOD(Error, setLayerLuts, (Display, Layer, aidl::android::hardware::graphics::composer3::Luts&)); + MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*)); + MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id)); + MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 121104d61c..fa74492ac5 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -52,7 +52,7 @@ public: (override)); MOCK_METHOD(hal::Error, getName, (std::string *), (const, override)); MOCK_METHOD(hal::Error, getRequests, - (hal::DisplayRequest *, (std::unordered_map *)), + (hal::DisplayRequest*, (std::unordered_map*)), (override)); MOCK_METHOD((ftl::Expected), getConnectionType, (), (const, override)); @@ -111,7 +111,9 @@ public: (aidl::android::hardware::graphics::composer3::OverlayProperties *), (const override)); MOCK_METHOD(hal::Error, getRequestedLuts, - ((HWC2::Display::LayerLuts*), (HWC2::Display::LutFileDescriptorMapper&)), + (HWC2::Display::LayerLuts*, HWC2::Display::LutFileDescriptorMapper&), (override)); + MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override)); + MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&), (override)); }; @@ -151,6 +153,8 @@ public: MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override)); MOCK_METHOD(hal::Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&), (override)); + MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&), + (override)); }; } // namespace android::HWC2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h index fa7128c557..88f83d2e07 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h @@ -148,6 +148,9 @@ public: MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); MOCK_METHOD(HWC2::Display::LutFileDescriptorMapper&, getLutFileDescriptorMapper, (), (override)); + MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId)); + MOCK_METHOD(status_t, setDisplayPictureProfileHandle, + (PhysicalDisplayId, const PictureProfileHandle&)); }; } // namespace android::mock -- cgit v1.2.3-59-g8ed1b From 3a28c3165d1a6c05c0e176b005eaa8596c487499 Mon Sep 17 00:00:00 2001 From: Vaibhav Devmurari Date: Mon, 4 Nov 2024 11:55:14 +0000 Subject: Add new 25Q2 keycodes to native Test: None Bug: 365920375 Flag: com.android.hardware.input.enable_new_25q2_keycodes Change-Id: If8d07744844eff38226e022dfb04195321a0976b --- include/android/keycodes.h | 38 ++++++++++++++++++++++++++++++++++++++ libs/input/InputEventLabels.cpp | 21 ++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/include/android/keycodes.h b/include/android/keycodes.h index 79cdbcaf7b..495e0bdb1f 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -843,6 +843,44 @@ enum { AKEYCODE_EMOJI_PICKER = 317, /** Take Screenshot */ AKEYCODE_SCREENSHOT = 318, + /** To start dictate to an input field */ + AKEYCODE_DICTATE = 319, + /** AC New */ + AKEYCODE_NEW = 320, + /** AC Close */ + AKEYCODE_CLOSE = 321, + /** To toggle 'Do Not Disturb' mode */ + AKEYCODE_DO_NOT_DISTURB = 322, + /** To Print */ + AKEYCODE_PRINT = 323, + /** To Lock the screen */ + AKEYCODE_LOCK = 324, + /** To toggle fullscreen mode (on the current application) */ + AKEYCODE_FULLSCREEN = 325, + /** F13 key */ + AKEYCODE_F13 = 326, + /** F14 key */ + AKEYCODE_F14 = 327, + /** F15 key */ + AKEYCODE_F15 = 328, + /** F16 key */ + AKEYCODE_F16 = 329, + /** F17 key */ + AKEYCODE_F17 = 330, + /** F18 key */ + AKEYCODE_F18 = 331, + /** F19 key */ + AKEYCODE_F19 = 332, + /** F20 key */ + AKEYCODE_F20 = 333, + /** F21 key */ + AKEYCODE_F21 = 334, + /** F22 key */ + AKEYCODE_F22 = 335, + /** F23 key */ + AKEYCODE_F23 = 336, + /** F24 key */ + AKEYCODE_F24 = 337, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index 8db0ca588b..b537feb68f 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -350,7 +350,26 @@ namespace android { DEFINE_KEYCODE(MACRO_3), \ DEFINE_KEYCODE(MACRO_4), \ DEFINE_KEYCODE(EMOJI_PICKER), \ - DEFINE_KEYCODE(SCREENSHOT) + DEFINE_KEYCODE(SCREENSHOT), \ + DEFINE_KEYCODE(DICTATE), \ + DEFINE_KEYCODE(NEW), \ + DEFINE_KEYCODE(CLOSE), \ + DEFINE_KEYCODE(DO_NOT_DISTURB), \ + DEFINE_KEYCODE(PRINT), \ + DEFINE_KEYCODE(LOCK), \ + DEFINE_KEYCODE(FULLSCREEN), \ + DEFINE_KEYCODE(F13), \ + DEFINE_KEYCODE(F14), \ + DEFINE_KEYCODE(F15), \ + DEFINE_KEYCODE(F16), \ + DEFINE_KEYCODE(F17), \ + DEFINE_KEYCODE(F18), \ + DEFINE_KEYCODE(F19),\ + DEFINE_KEYCODE(F20), \ + DEFINE_KEYCODE(F21), \ + DEFINE_KEYCODE(F22), \ + DEFINE_KEYCODE(F23), \ + DEFINE_KEYCODE(F24) // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. -- cgit v1.2.3-59-g8ed1b From 1d038d478f8f9ddee71e2fe90ac97ed96303f554 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 29 Oct 2024 11:25:58 -0700 Subject: Consume full tap in EndToEndNativeInputTest Currently, the injection inside the test is happening asynchronously. Occasionally, it seems that an inconsistent event stream is being sent. Without further debugging about how this is possible, a speculative fix is being made here by consuming a complete motion sequence after a tap is injected. This way, we know that the test must wait for the UP event to be received, thus hopefully preventing the next test from running and starting the asynchronous injection. To prevent misuse of 'consumeEvent' in the future, it is also being made private in this CL. Unfortunately, a bunch of other fields should also be private (but were not made so in the beginning, possibly a typo). As a result, some of the fields are now relied upon. These are kept as is to keep focus of this CL. Bug: 361264974 Test: atest libgui_test Flag: TEST_ONLY Change-Id: I99bac4022bab2b3aa106e0838b227f24eb512ea1 --- libs/gui/tests/EndToEndNativeInputTest.cpp | 38 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'libs') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 17630e304f..2f58a6cc8f 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -183,20 +183,6 @@ public: return std::make_unique(surfaceControl, width, height); } - InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) { - mClientChannel->waitForMessage(timeout); - - InputEvent* ev; - uint32_t seqId; - status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev); - if (consumed != OK) { - return nullptr; - } - status_t status = mInputConsumer->sendFinishedSignal(seqId, true); - EXPECT_EQ(OK, status) << "Could not send finished signal"; - return ev; - } - void assertFocusChange(bool hasFocus) { InputEvent* ev = consumeEvent(); ASSERT_NE(ev, nullptr); @@ -323,14 +309,30 @@ public: } public: + // But should be private + WindowInfo mInputInfo; sp mSurfaceControl; + +private: std::shared_ptr mClientChannel; sp mInputFlinger; - WindowInfo mInputInfo; - PreallocatedInputEventFactory mInputEventFactory; InputConsumer* mInputConsumer; + + InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) { + mClientChannel->waitForMessage(timeout); + + InputEvent* ev; + uint32_t seqId; + status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev); + if (consumed != OK) { + return nullptr; + } + status_t status = mInputConsumer->sendFinishedSignal(seqId, true); + EXPECT_EQ(OK, status) << "Could not send finished signal"; + return ev; + } }; class BlastInputSurface : public InputSurface { @@ -458,7 +460,7 @@ TEST_F(InputSurfacesTest, can_receive_input) { injectTap(101, 101); - EXPECT_NE(surface->consumeEvent(), nullptr); + surface->expectTap(1, 1); } /** @@ -612,7 +614,7 @@ TEST_F(InputSurfacesTest, touchable_region) { // A tap within the surface but outside the touchable region should not be sent to the surface. injectTap(20, 30); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr); + surface->assertNoEvent(); injectTap(31, 52); surface->expectTap(20, 30); -- cgit v1.2.3-59-g8ed1b From 0980aa8675918ac2c18acca4668db683a93c65be Mon Sep 17 00:00:00 2001 From: Justin Lannin Date: Wed, 6 Nov 2024 07:25:00 +0000 Subject: ASM: Add required AppOp lookup to custom sensors for all permissions. Previously, we only made an AppOp lookup if the custom sensor used BODY_SENSORS. We are deprecating BODY_SENSORS in favor of READ_HEART_RATE and other granular health permissions. This CL ensures the correct AppOp is used if the custom sensor uses these other permissions. In the spirit of flagging, we guard the expanded AppOp lookup behind the replace_body_sensor_permission_enabled flag. However, it's worth noting that any permission with an existing AppOp would now be used (not just READ_HEART_RATE). Bug: 377576685 Change-Id: Ia16663795479ee49d5b5a82cd5b304b6dd6a26f1 Flag: android.permission.flags.replace_body_sensor_permission_enabled --- libs/sensor/Sensor.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index eddd568fb5..797efbe5df 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -306,7 +306,18 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi } if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) { mRequiredPermission = hwSensor.requiredPermission; - if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) { + bool requiresBodySensorPermission = + !strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS); + if (android::permission::flags::replace_body_sensor_permission_enabled()) { + if (requiresBodySensorPermission) { + ALOGE("Sensor %s using deprecated Body Sensor permission", mName.c_str()); + } + + AppOpsManager appOps; + // Lookup to see if an AppOp exists for the permission. If none + // does, the default value of -1 is used. + mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission)); + } else if (requiresBodySensorPermission) { AppOpsManager appOps; mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS)); } -- cgit v1.2.3-59-g8ed1b From 87602161374f8dcede06e7280f267f77c2bee3c5 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Tue, 5 Nov 2024 10:13:19 -0600 Subject: Update Transaction::setInputWindowInfo to take WindowInfoHandle This change allows us to reuse WindowInfoHandle objects instead of allocating a new WindowInfoHandle object for each call. Bug: 294381558 Flag: EXEMPT refactor Test: presubmits Change-Id: I39d965217763a9cacfc9e77d0723200038fd2afe --- libs/gui/SurfaceComposerClient.cpp | 4 +- libs/gui/include/gui/SurfaceComposerClient.h | 3 +- libs/gui/tests/EndToEndNativeInputTest.cpp | 107 ++++++++++++--------- services/surfaceflinger/tests/Credentials_test.cpp | 12 ++- .../tests/WindowInfosListener_test.cpp | 24 ++--- 5 files changed, 82 insertions(+), 68 deletions(-) (limited to 'libs') diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 3260c53a62..c97dfd4cda 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -2129,13 +2129,13 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( - const sp& sc, const WindowInfo& info) { + const sp& sc, sp info) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->windowInfoHandle = new WindowInfoHandle(info); + s->windowInfoHandle = std::move(info); s->what |= layer_state_t::eInputInfoChanged; return *this; } diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index e9262b3870..7c6b3416f8 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -687,7 +687,8 @@ public: // ONLY FOR BLAST ADAPTER Transaction& notifyProducerDisconnect(const sp& sc); - Transaction& setInputWindowInfo(const sp& sc, const gui::WindowInfo& info); + Transaction& setInputWindowInfo(const sp& sc, + sp info); Transaction& setFocusedWindow(const gui::FocusRequest& request); Transaction& addWindowInfosReportedListener( diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 2f58a6cc8f..0e84d68eec 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -112,7 +112,7 @@ public: mInputFlinger = getInputFlinger(); if (noInputChannel) { - mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); + mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); } else { android::os::InputChannelCore tempChannel; android::binder::Status result = @@ -121,21 +121,21 @@ public: ADD_FAILURE() << "binder call to createInputChannel failed"; } mClientChannel = InputChannel::create(std::move(tempChannel)); - mInputInfo.token = mClientChannel->getConnectionToken(); + mInputInfo->editInfo()->token = mClientChannel->getConnectionToken(); mInputConsumer = new InputConsumer(mClientChannel); } - mInputInfo.name = "Test info"; - mInputInfo.dispatchingTimeout = 5s; - mInputInfo.globalScaleFactor = 1.0; - mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height)); + mInputInfo->editInfo()->name = "Test info"; + mInputInfo->editInfo()->dispatchingTimeout = 5s; + mInputInfo->editInfo()->globalScaleFactor = 1.0; + mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, width, height)); InputApplicationInfo aInfo; aInfo.token = new BBinder(); aInfo.name = "Test app info"; aInfo.dispatchingTimeoutMillis = std::chrono::duration_cast(DISPATCHING_TIMEOUT).count(); - mInputInfo.applicationInfo = aInfo; + mInputInfo->editInfo()->applicationInfo = aInfo; } static std::unique_ptr makeColorInputSurface(const sp& scc, @@ -300,8 +300,8 @@ public: void requestFocus(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) { SurfaceComposerClient::Transaction t; FocusRequest request; - request.token = mInputInfo.token; - request.windowName = mInputInfo.name; + request.token = mInputInfo->getInfo()->token; + request.windowName = mInputInfo->getInfo()->name; request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId.val(); t.setFocusedWindow(request); @@ -310,7 +310,7 @@ public: public: // But should be private - WindowInfo mInputInfo; + sp mInputInfo = sp::make(); sp mSurfaceControl; private: @@ -523,7 +523,7 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { std::unique_ptr fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; fgSurface->showAt(100, 100); injectTap(106, 106); @@ -538,8 +538,8 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableReg std::unique_ptr fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; - fgSurface->mInputInfo.replaceTouchableRegionWithCrop = true; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; fgSurface->showAt(100, 100); injectTap(106, 106); @@ -555,7 +555,7 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { std::unique_ptr childSurface = makeSurface(100, 100); parentSurface->showAt(100, 100); - childSurface->mInputInfo.surfaceInset = 10; + childSurface->mInputInfo->editInfo()->surfaceInset = 10; childSurface->showAt(100, 100); childSurface->doTransaction([&](auto& t, auto& sc) { @@ -576,7 +576,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { std::unique_ptr fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; fgSurface->showAt(100, 100); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); }); @@ -595,7 +595,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { bgSurface->showAt(100, 100); // In case we pass the very big inset without any checking. - fgSurface->mInputInfo.surfaceInset = INT32_MAX; + fgSurface->mInputInfo->editInfo()->surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -608,7 +608,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { TEST_F(InputSurfacesTest, touchable_region) { std::unique_ptr surface = makeSurface(100, 100); - surface->mInputInfo.touchableRegion.set(Rect{19, 29, 21, 31}); + surface->mInputInfo->editInfo()->touchableRegion.set(Rect{19, 29, 21, 31}); surface->showAt(11, 22); @@ -629,7 +629,8 @@ TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) { // Since the surface is offset from the origin, the touchable region will be transformed into // display space, which would trigger an overflow or an underflow. Ensure that we are protected // against such a situation. - fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); + fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf( + Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); fgSurface->showAt(100, 100); @@ -644,7 +645,8 @@ TEST_F(InputSurfacesTest, input_respects_scaled_touchable_region_overflow) { std::unique_ptr fgSurface = makeSurface(100, 100); bgSurface->showAt(0, 0); - fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); + fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf( + Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); fgSurface->showAt(0, 0); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -814,7 +816,7 @@ TEST_F(InputSurfacesTest, rotate_surface_with_scale) { TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) { std::unique_ptr surface = makeSurface(100, 100); - surface->mInputInfo.surfaceInset = 5; + surface->mInputInfo->editInfo()->surfaceInset = 5; surface->showAt(100, 100); surface->doTransaction([](auto& t, auto& sc) { @@ -843,11 +845,12 @@ TEST_F(InputSurfacesTest, touch_flag_obscured) { // Add non touchable window to fully cover touchable window. Window behind gets touch, but // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED std::unique_ptr nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by // the default obscured/untrusted touch filter introduced in S. - nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW; + nonTouchableSurface->mInputInfo->editInfo()->touchOcclusionMode = TouchOcclusionMode::ALLOW; nonTouchableSurface->showAt(100, 100); injectTap(190, 199); @@ -863,10 +866,12 @@ TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) { // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED std::unique_ptr parentSurface = makeSurface(100, 100); std::unique_ptr nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; - parentSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; + parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; nonTouchableSurface->showAt(0, 0); parentSurface->showAt(100, 100); @@ -887,10 +892,12 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) { // the touchable window. Window behind gets touch with no obscured flags. std::unique_ptr parentSurface = makeSurface(100, 100); std::unique_ptr nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; - parentSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; + parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; nonTouchableSurface->showAt(0, 0); parentSurface->showAt(50, 50); @@ -908,8 +915,9 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_bql) { std::unique_ptr bufferSurface = InputSurface::makeBufferInputSurface(mComposerClient, 0, 0); - bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - bufferSurface->mInputInfo.ownerUid = gui::Uid{22222}; + bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; surface->showAt(10, 10); bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); @@ -923,8 +931,9 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) { std::unique_ptr bufferSurface = BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); - bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - bufferSurface->mInputInfo.ownerUid = gui::Uid{22222}; + bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; surface->showAt(10, 10); bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); @@ -967,13 +976,14 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) { TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { std::unique_ptr surface = makeSurface(100, 100); - surface->mInputInfo.ownerUid = gui::Uid{11111}; + surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111}; surface->doTransaction( [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); surface->showAt(100, 100); std::unique_ptr obscuringSurface = makeSurface(100, 100); - obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; + obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; obscuringSurface->showAt(100, 100); injectTap(101, 101); surface->assertNoEvent(); @@ -986,13 +996,14 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { std::unique_ptr surface = makeSurface(100, 100); - surface->mInputInfo.ownerUid = gui::Uid{11111}; + surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111}; surface->doTransaction( [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); surface->showAt(100, 100); std::unique_ptr obscuringSurface = makeSurface(100, 100); - obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; + obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; obscuringSurface->showAt(190, 190); injectTap(101, 101); @@ -1056,7 +1067,7 @@ TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) { BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); surface->showAt(100, 100); - bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200)); + bufferSurface->mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, 200, 200)); bufferSurface->showAt(100, 100, Rect::EMPTY_RECT); injectTap(101, 101); @@ -1099,8 +1110,8 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr; parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect(0, 0, 5, 5)); @@ -1126,8 +1137,8 @@ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_nul InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr; parentContainer->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, bgContainer->mSurfaceControl); }); bgContainer->showAt(0, 0, Rect(0, 0, 100, 100)); @@ -1154,8 +1165,8 @@ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { std::unique_ptr containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = cropLayer->mSurfaceControl->getHandle(); containerSurface->showAt(10, 10, Rect::INVALID_RECT); diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index e6fed63d96..7b6e4bff6a 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -341,9 +341,9 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { WindowInfosListenerUtils windowInfosListenerUtils; std::string name = "Test Layer"; sp token = sp::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp surfaceControl = mComposerClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -370,7 +370,8 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { UIDFaker f(AID_SYSTEM); auto windowIsPresentAndNotTrusted = [&](const std::vector& windowInfos) { auto foundWindowInfo = - WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos); + WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(), + windowInfos); if (!foundWindowInfo) { return false; } @@ -386,7 +387,8 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { Transaction().setTrustedOverlay(surfaceControl, true).apply(/*synchronous=*/true); auto windowIsPresentAndTrusted = [&](const std::vector& windowInfos) { auto foundWindowInfo = - WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos); + WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(), + windowInfos); if (!foundWindowInfo) { return false; } diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp index ad9a674456..2dd0dd9bd3 100644 --- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp +++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp @@ -50,9 +50,9 @@ protected: TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { std::string name = "Test Layer"; sp token = sp::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp surfaceControl = mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -65,14 +65,14 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { .apply(); auto windowPresent = [&](const std::vector& windowInfos) { - return findMatchingWindowInfo(windowInfo, windowInfos); + return findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent)); Transaction().reparent(surfaceControl, nullptr).apply(); auto windowNotPresent = [&](const std::vector& windowInfos) { - return !findMatchingWindowInfo(windowInfo, windowInfos); + return !findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent)); } @@ -80,9 +80,9 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { TEST_F(WindowInfosListenerTest, WindowInfoChanged) { std::string name = "Test Layer"; sp token = sp::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp surfaceControl = mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -96,7 +96,7 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { .apply(); auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector& windowInfos) { - auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); if (!foundWindowInfo) { return false; } @@ -104,19 +104,19 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { }; ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty)); - windowInfo.addTouchableRegion({0, 0, 50, 50}); + windowInfo->editInfo()->addTouchableRegion({0, 0, 50, 50}); Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply(); auto windowIsPresentAndTouchableRegionMatches = [&](const std::vector& windowInfos) { - auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); if (!foundWindowInfo) { return false; } auto touchableRegion = foundWindowInfo->transform.transform(foundWindowInfo->touchableRegion); - return touchableRegion.hasSameRects(windowInfo.touchableRegion); + return touchableRegion.hasSameRects(windowInfo->getInfo()->touchableRegion); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches)); } -- cgit v1.2.3-59-g8ed1b From 07dcd4977f47e37d8dd24cf7abc32202fbe088df Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Wed, 30 Oct 2024 11:43:23 -0600 Subject: Allow apps to apply picture profiles with priority to layers Bug: 337330263 Test: build Test: atest LayerSnapshotTest Flag: com.android.graphics.libgui.flags.apply_picture_profiles Change-Id: I1adb6069d0168084abf0a76d310abb4ffad5ce5f --- libs/gui/LayerState.cpp | 27 +++++++++++++- libs/gui/SurfaceComposerClient.cpp | 37 +++++++++++++++++-- libs/gui/include/gui/LayerState.h | 15 +++++++- libs/gui/include/gui/SurfaceComposerClient.h | 15 ++++++++ libs/ui/include/ui/PictureProfileHandle.h | 2 +- services/surfaceflinger/FrontEnd/LayerSnapshot.cpp | 7 ++++ services/surfaceflinger/SurfaceFlinger.cpp | 6 +++- .../tests/unittests/LayerSnapshotTest.cpp | 42 +++++++++++++++++++++- 8 files changed, 144 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 4b531345b0..1c527d23b6 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -91,7 +92,9 @@ layer_state_t::layer_state_t() trustedOverlay(gui::TrustedOverlay::UNSET), bufferCrop(Rect::INVALID_RECT), destinationFrame(Rect::INVALID_RECT), - dropInputMode(gui::DropInputMode::NONE) { + dropInputMode(gui::DropInputMode::NONE), + pictureProfileHandle(PictureProfileHandle::NONE), + appContentPriority(0) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; hdrMetadata.validTypes = 0; @@ -202,6 +205,10 @@ status_t layer_state_t::write(Parcel& output) const if (hasBufferReleaseChannel) { SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES + SAFE_PARCEL(output.writeInt64, pictureProfileHandle.getId()); + SAFE_PARCEL(output.writeInt32, appContentPriority); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES return NO_ERROR; } @@ -357,6 +364,12 @@ status_t layer_state_t::read(const Parcel& input) bufferReleaseChannel = std::make_shared(); SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get()); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES + int64_t pictureProfileId; + SAFE_PARCEL(input.readInt64, &pictureProfileId); + pictureProfileHandle = PictureProfileHandle(pictureProfileId); + SAFE_PARCEL(input.readInt32, &appContentPriority); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES return NO_ERROR; } @@ -745,6 +758,16 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eBufferReleaseChannelChanged; bufferReleaseChannel = other.bufferReleaseChannel; } + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + if (other.what & ePictureProfileHandleChanged) { + what |= ePictureProfileHandleChanged; + pictureProfileHandle = other.pictureProfileHandle; + } + if (other.what & eAppContentPriorityChanged) { + what |= eAppContentPriorityChanged; + appContentPriority = other.appContentPriority; + } + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64, @@ -826,6 +849,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled); if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged; if (other.what & eLutsChanged) diff |= eLutsChanged; + CHECK_DIFF(diff, ePictureProfileHandleChanged, other, pictureProfileHandle); + CHECK_DIFF(diff, eAppContentPriorityChanged, other, appContentPriority); return diff; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 3260c53a62..ebed5767a4 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -20,8 +20,6 @@ #include #include -#include - #include #include #include @@ -29,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -2447,6 +2446,40 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPictureProfileHandle( + const sp& sc, const PictureProfileHandle& pictureProfileHandle) { + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::ePictureProfileHandleChanged; + s->pictureProfileHandle = pictureProfileHandle; + + registerSurfaceControlForCallback(sc); + } + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setContentPriority( + const sp& sc, int32_t priority) { + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eAppContentPriorityChanged; + s->appContentPriority = priority; + + registerSurfaceControlForCallback(sc); + } + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp& token) { diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 6bfeaec26a..9098dffa8e 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -224,6 +225,8 @@ struct layer_state_t { eExtendedRangeBrightnessChanged = 0x10000'00000000, eEdgeExtensionChanged = 0x20000'00000000, eBufferReleaseChannelChanged = 0x40000'00000000, + ePictureProfileHandleChanged = 0x80000'00000000, + eAppContentPriorityChanged = 0x100000'00000000, }; layer_state_t(); @@ -267,7 +270,8 @@ struct layer_state_t { layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged | layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged | - layer_state_t::eStretchChanged; + layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged | + layer_state_t::eAppContentPriorityChanged; // Changes which invalidates the layer's visible region in CE. static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES | @@ -412,6 +416,15 @@ struct layer_state_t { float currentHdrSdrRatio = 1.f; float desiredHdrSdrRatio = 1.f; + // Enhance the quality of the buffer contents by configurating a picture processing pipeline + // with values as specified by this picture profile. + PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE}; + + // A value indicating the significance of the layer's content to the app's desired user + // experience. A lower priority will result in more likelihood of getting access to limited + // resources, such as picture processing hardware. + int32_t appContentPriority = 0; + gui::CachingHint cachingHint = gui::CachingHint::Enabled; TrustedPresentationThresholds trustedPresentationThresholds; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index e9262b3870..6968d252e8 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -775,6 +776,20 @@ public: const sp& sc, const std::shared_ptr& channel); + /** + * Configures a surface control to use picture processing hardware, configured as specified + * by the picture profile, to enhance the quality of all subsequent buffer contents. + */ + Transaction& setPictureProfileHandle(const sp& sc, + const PictureProfileHandle& pictureProfileHandle); + + /** + * Configures the relative importance of the contents of the layer with respect to the app's + * user experience. A lower priority value will give the layer preferred access to limited + * resources, such as picture processing, over a layer with a higher priority value. + */ + Transaction& setContentPriority(const sp& sc, int32_t contentPriority); + status_t setDisplaySurface(const sp& token, const sp& bufferProducer); diff --git a/libs/ui/include/ui/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h index 9b709b6155..f8406501b4 100644 --- a/libs/ui/include/ui/PictureProfileHandle.h +++ b/libs/ui/include/ui/PictureProfileHandle.h @@ -39,7 +39,7 @@ public: static const PictureProfileHandle NONE; PictureProfileHandle() { *this = NONE; } - PictureProfileHandle(PictureProfileId id) : mId(id) {} + explicit PictureProfileHandle(PictureProfileId id) : mId(id) {} PictureProfileId const& getId() const { return mId; } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index a8be50a074..58f6b96e57 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -413,6 +413,13 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate if (forceUpdate || requested.what & layer_state_t::eCropChanged) { geomCrop = requested.crop; } + if (forceUpdate || requested.what & layer_state_t::ePictureProfileHandleChanged) { + pictureProfileHandle = requested.pictureProfileHandle; + } + if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) { + // TODO(b/337330263): Also consider the system-determined priority of the app + pictureProfilePriority = requested.appContentPriority; + } if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) { const auto compatibility = diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index a7ab117679..0d03a6cef3 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3558,7 +3558,9 @@ std::optional SurfaceFlinger::processHotplugConnect(PhysicalDispl } state.isProtected = true; state.displayName = std::move(info.name); - + state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId); + state.hasPictureProcessing = + getHwComposer().hasDisplayCapability(displayId, DisplayCapability::PICTURE_PROCESSING); mCurrentState.displays.add(token, state); ALOGI("Connecting %s", displayString); return activeModeId; @@ -3719,6 +3721,8 @@ void SurfaceFlinger::processDisplayAdded(const wp& displayToken, builder.setPixels(resolution); builder.setIsSecure(state.isSecure); builder.setIsProtected(state.isProtected); + builder.setHasPictureProcessing(state.hasPictureProcessing); + builder.setMaxLayerPictureProfiles(state.maxLayerPictureProfiles); builder.setPowerAdvisor(mPowerAdvisor.get()); builder.setName(state.displayName); auto compositionDisplay = getCompositionEngine().createDisplay(builder.build()); diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index e6b8a26487..6aec7434de 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -28,7 +28,6 @@ #include "ui/GraphicTypes.h" #include -#include #define UPDATE_AND_VERIFY(BUILDER, ...) \ ({ \ @@ -1985,4 +1984,45 @@ TEST_F(LayerSnapshotTest, contentDirtyWhenParentGeometryChanges) { UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); EXPECT_FALSE(getSnapshot(1)->contentDirty); } +TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Flag disabled, skipping test"; + } + std::vector transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().layerId = 1; + transactions.back().states.front().state.layerId = 1; + transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged; + transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3); + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content); + + update(mSnapshotBuilder); + + EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::ePictureProfileHandleChanged); + EXPECT_EQ(getSnapshot(1)->pictureProfileHandle, PictureProfileHandle(3)); +} + +TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Flag disabled, skipping test"; + } + std::vector transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().layerId = 1; + transactions.back().states.front().state.layerId = 1; + transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged; + transactions.back().states.front().state.appContentPriority = 3; + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content); + + update(mSnapshotBuilder); + + EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3); +} + } // namespace android::surfaceflinger::frontend -- cgit v1.2.3-59-g8ed1b From 628cff4cec8899b1c9cf75d4a3ae80617b97d825 Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Wed, 30 Oct 2024 11:50:28 -0600 Subject: Allow apps to associate a change in picture profiles alongside a buffer What picture processing a buffer looks best with is often dependent on the buffer contents itself. It is often necessary for a change in picture profile to be tightly coupled to a change in buffer. Bug: 337330263 Test: build Test: atest BufferQueueTest Flag: com.android.graphics.libgui.flags.apply_picture_profiles Change-Id: I8bd3468519fb28a234f6853531638e348b1c5274 --- libs/gui/BLASTBufferQueue.cpp | 18 ++++++- libs/gui/BufferItem.cpp | 65 +++++++++++++++++-------- libs/gui/BufferQueueProducer.cpp | 3 ++ libs/gui/IGraphicBufferProducerFlattenables.cpp | 37 +++++++++----- libs/gui/include/gui/BLASTBufferQueue.h | 9 +++- libs/gui/include/gui/BufferItem.h | 7 +++ libs/gui/include/gui/IGraphicBufferProducer.h | 11 +++++ libs/gui/tests/BufferQueue_test.cpp | 58 ++++++++++++++++++++++ 8 files changed, 173 insertions(+), 35 deletions(-) (limited to 'libs') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 495418b921..7aee90393b 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -286,18 +286,23 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, 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; SurfaceComposerClient::Transaction t; + bool applyTransaction = false; if (surfaceControlChanged) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) updateBufferReleaseProducer(); #endif t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); + // Migrate the picture profile handle to the new surface control. + if (com_android_graphics_libgui_flags_apply_picture_profiles() && + mPictureProfileHandle.has_value()) { + t.setPictureProfileHandle(mSurfaceControl, *mPictureProfileHandle); + } applyTransaction = true; } mTransformHint = mSurfaceControl->getTransformHint(); @@ -679,6 +684,17 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( if (!bufferItem.mIsAutoTimestamp) { t->setDesiredPresentTime(bufferItem.mTimestamp); } + if (com_android_graphics_libgui_flags_apply_picture_profiles() && + bufferItem.mPictureProfileHandle.has_value()) { + t->setPictureProfileHandle(mSurfaceControl, *bufferItem.mPictureProfileHandle); + // The current picture profile must be maintained in case the BBQ gets its + // SurfaceControl switched out. + mPictureProfileHandle = bufferItem.mPictureProfileHandle; + // Clear out the picture profile if the requestor has asked for it to be cleared + if (mPictureProfileHandle == PictureProfileHandle::NONE) { + mPictureProfileHandle = std::nullopt; + } + } // Drop stale frame timeline infos while (!mPendingFrameTimelines.empty() && diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index 5beba02e63..3b2d337a21 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -38,26 +38,25 @@ static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { return static_cast(static_cast(hi)<<32 | lo); } -BufferItem::BufferItem() : - mGraphicBuffer(nullptr), - mFence(nullptr), - mCrop(Rect::INVALID_RECT), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), - mIsAutoTimestamp(false), - mDataSpace(HAL_DATASPACE_UNKNOWN), - mFrameNumber(0), - mSlot(INVALID_BUFFER_SLOT), - mIsDroppable(false), - mAcquireCalled(false), - mTransformToDisplayInverse(false), - mSurfaceDamage(), - mAutoRefresh(false), - mQueuedBuffer(true), - mIsStale(false), - mApi(0) { -} +BufferItem::BufferItem() + : mGraphicBuffer(nullptr), + mFence(nullptr), + mCrop(Rect::INVALID_RECT), + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mIsAutoTimestamp(false), + mDataSpace(HAL_DATASPACE_UNKNOWN), + mFrameNumber(0), + mSlot(INVALID_BUFFER_SLOT), + mIsDroppable(false), + mAcquireCalled(false), + mTransformToDisplayInverse(false), + mSurfaceDamage(), + mAutoRefresh(false), + mQueuedBuffer(true), + mIsStale(false), + mApi(0) {} BufferItem::~BufferItem() {} @@ -76,6 +75,11 @@ size_t BufferItem::getPodSize() const { addAligned(size, high32(mTimestamp)); addAligned(size, mIsAutoTimestamp); addAligned(size, mDataSpace); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + addAligned(size, mPictureProfileHandle.has_value()); + addAligned(size, low32(PictureProfileHandle::NONE.getId())); + addAligned(size, high32(PictureProfileHandle::NONE.getId())); +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES addAligned(size, low32(mFrameNumber)); addAligned(size, high32(mFrameNumber)); addAligned(size, mSlot); @@ -170,6 +174,16 @@ status_t BufferItem::flatten( writeAligned(buffer, size, high32(mTimestamp)); writeAligned(buffer, size, mIsAutoTimestamp); writeAligned(buffer, size, mDataSpace); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + writeAligned(buffer, size, mPictureProfileHandle.has_value()); + if (mPictureProfileHandle.has_value()) { + writeAligned(buffer, size, low32(mPictureProfileHandle->getId())); + writeAligned(buffer, size, high32(mPictureProfileHandle->getId())); + } else { + writeAligned(buffer, size, low32(PictureProfileHandle::NONE.getId())); + writeAligned(buffer, size, high32(PictureProfileHandle::NONE.getId())); + } +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES writeAligned(buffer, size, low32(mFrameNumber)); writeAligned(buffer, size, high32(mFrameNumber)); writeAligned(buffer, size, mSlot); @@ -231,6 +245,7 @@ status_t BufferItem::unflatten( uint32_t timestampLo = 0, timestampHi = 0; uint32_t frameNumberLo = 0, frameNumberHi = 0; + int32_t pictureProfileIdLo = 0, pictureProfileIdHi = 0; readAligned(buffer, size, mCrop); readAligned(buffer, size, mTransform); @@ -240,6 +255,16 @@ status_t BufferItem::unflatten( mTimestamp = to64(timestampLo, timestampHi); readAligned(buffer, size, mIsAutoTimestamp); readAligned(buffer, size, mDataSpace); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + bool hasPictureProfileHandle; + readAligned(buffer, size, hasPictureProfileHandle); + readAligned(buffer, size, pictureProfileIdLo); + readAligned(buffer, size, pictureProfileIdHi); + mPictureProfileHandle = hasPictureProfileHandle + ? std::optional(PictureProfileHandle( + to64(pictureProfileIdLo, pictureProfileIdHi))) + : std::nullopt; +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES readAligned(buffer, size, frameNumberLo); readAligned(buffer, size, frameNumberHi); mFrameNumber = to64(frameNumberLo, frameNumberHi); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 473a374a59..39209f9745 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -938,6 +938,8 @@ status_t BufferQueueProducer::queueBuffer(int slot, &getFrameTimestamps); const Region& surfaceDamage = input.getSurfaceDamage(); const HdrMetadata& hdrMetadata = input.getHdrMetadata(); + const std::optional& pictureProfileHandle = + input.getPictureProfileHandle(); if (acquireFence == nullptr) { BQ_LOGE("queueBuffer: fence is NULL"); @@ -1044,6 +1046,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, item.mIsAutoTimestamp = isAutoTimestamp; item.mDataSpace = dataSpace; item.mHdrMetadata = hdrMetadata; + item.mPictureProfileHandle = pictureProfileHandle; item.mFrameNumber = currentFrameNumber; item.mSlot = slot; item.mFence = acquireFence; diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp index c8b9b6751d..4e92a39973 100644 --- a/libs/gui/IGraphicBufferProducerFlattenables.cpp +++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp @@ -20,21 +20,19 @@ namespace android { constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() { - return sizeof(timestamp) + - sizeof(isAutoTimestamp) + - sizeof(dataSpace) + - sizeof(crop) + - sizeof(scalingMode) + - sizeof(transform) + - sizeof(stickyTransform) + - sizeof(getFrameTimestamps) + - sizeof(slot); + return sizeof(timestamp) + sizeof(isAutoTimestamp) + sizeof(dataSpace) + sizeof(crop) + + sizeof(scalingMode) + sizeof(transform) + sizeof(stickyTransform) + + sizeof(getFrameTimestamps) + sizeof(slot) + +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + sizeof(decltype(pictureProfileHandle.has_value())) + + sizeof(decltype(pictureProfileHandle.getId())); +#else + 0; +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES } size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { - return minFlattenedSize() + - fence->getFlattenedSize() + - surfaceDamage.getFlattenedSize() + + return minFlattenedSize() + fence->getFlattenedSize() + surfaceDamage.getFlattenedSize() + hdrMetadata.getFlattenedSize(); } @@ -57,6 +55,12 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten( FlattenableUtils::write(buffer, size, transform); FlattenableUtils::write(buffer, size, stickyTransform); FlattenableUtils::write(buffer, size, getFrameTimestamps); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + FlattenableUtils::write(buffer, size, pictureProfileHandle.has_value()); + FlattenableUtils::write(buffer, size, + pictureProfileHandle.has_value() ? pictureProfileHandle->getId() + : PictureProfileHandle::NONE.getId()); +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES status_t result = fence->flatten(buffer, size, fds, count); if (result != NO_ERROR) { @@ -91,6 +95,15 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( FlattenableUtils::read(buffer, size, transform); FlattenableUtils::read(buffer, size, stickyTransform); FlattenableUtils::read(buffer, size, getFrameTimestamps); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + bool hasPictureProfileHandle; + FlattenableUtils::read(buffer, size, hasPictureProfileHandle); + PictureProfileId pictureProfileId; + FlattenableUtils::read(buffer, size, pictureProfileId); + pictureProfileHandle = hasPictureProfileHandle + ? std::optional(PictureProfileHandle(pictureProfileId)) + : std::nullopt; +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES fence = new Fence(); status_t result = fence->unflatten(buffer, size, fds, count); diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 8894b66c6d..07558aa49d 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -17,7 +17,9 @@ #ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H #define ANDROID_GUI_BLAST_BUFFER_QUEUE_H -#include +#include +#include + #include #include #include @@ -29,7 +31,6 @@ #include #include -#include #include @@ -222,6 +223,10 @@ private: ui::Size mRequestedSize GUARDED_BY(mMutex); int32_t mFormat GUARDED_BY(mMutex); + // Keep a copy of the current picture profile handle, so it can be moved to a new + // SurfaceControl when BBQ migrates via ::update. + std::optional mPictureProfileHandle; + struct BufferInfo { bool hasBuffer = false; uint32_t width; diff --git a/libs/gui/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h index 218bb424fb..2f85c62a54 100644 --- a/libs/gui/include/gui/BufferItem.h +++ b/libs/gui/include/gui/BufferItem.h @@ -17,9 +17,12 @@ #ifndef ANDROID_GUI_BUFFERITEM_H #define ANDROID_GUI_BUFFERITEM_H +#include + #include #include +#include #include #include @@ -91,6 +94,10 @@ class BufferItem : public Flattenable { // mHdrMetadata is the HDR metadata associated with this buffer slot. HdrMetadata mHdrMetadata; + // mPictureProfileHandle is a handle that points to a set of parameters that configure picture + // processing hardware to enhance the quality of buffer contents. + std::optional mPictureProfileHandle; + // mFrameNumber is the number of the queued frame for this slot. uint64_t mFrameNumber; diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 3aac457a09..001e570982 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include @@ -365,6 +367,14 @@ public: const HdrMetadata& getHdrMetadata() const { return hdrMetadata; } void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; } + const std::optional& getPictureProfileHandle() const { + return pictureProfileHandle; + } + void setPictureProfileHandle(const PictureProfileHandle& profile) { + pictureProfileHandle = profile; + } + void clearPictureProfileHandle() { pictureProfileHandle = std::nullopt; } + int64_t timestamp{0}; int isAutoTimestamp{0}; android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN}; @@ -377,6 +387,7 @@ public: bool getFrameTimestamps{false}; int slot{-1}; HdrMetadata hdrMetadata; + std::optional pictureProfileHandle; }; struct QueueBufferOutput : public Flattenable { diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 2e6ffcb57f..b026e640aa 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -27,6 +27,7 @@ #include #include +#include #include @@ -1569,4 +1570,61 @@ TEST_F(BufferQueueTest, TestAdditionalOptions) { EXPECT_EQ(ADATASPACE_UNKNOWN, dataSpace); } +TEST_F(BufferQueueTest, PassesThroughPictureProfileHandle) { + createBufferQueue(); + sp mc(new MockConsumer); + mConsumer->consumerConnect(mc, false); + + IGraphicBufferProducer::QueueBufferOutput qbo; + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo); + mProducer->setMaxDequeuedBufferCount(2); + mConsumer->setMaxAcquiredBufferCount(2); + + // First try to pass a valid picture profile handle + { + int slot; + sp fence; + sp buf; + IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN, + Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + qbi.setPictureProfileHandle(PictureProfileHandle(1)); + + EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); + EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf)); + EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); + + BufferItem item; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + + ASSERT_TRUE(item.mPictureProfileHandle.has_value()); + ASSERT_EQ(item.mPictureProfileHandle, PictureProfileHandle(1)); + } + + // Then validate that the picture profile handle isn't sticky and is reset for the next buffer + { + int slot; + sp fence; + sp buf; + IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN, + Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + + EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); + EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf)); + EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); + + BufferItem item; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + + ASSERT_FALSE(item.mPictureProfileHandle.has_value()); + } +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From ef006586b5e3bbbf69177958a388cb1208adf0ff Mon Sep 17 00:00:00 2001 From: Sally Qi Date: Fri, 11 Oct 2024 13:23:09 -0700 Subject: [Lut HAL backend] implementation 3rd patch. - interpret the lut and pass them into shader. Bug: 329472856 Test: builds Flag: EXEMPT no flag needed Change-Id: I005600593f4a369130bf8bcaea69300758b5ae03 --- libs/gui/Android.bp | 1 + libs/gui/DisplayLuts.cpp | 81 +++++++ libs/gui/LayerState.cpp | 15 ++ libs/gui/SurfaceComposerClient.cpp | 8 +- libs/gui/include/gui/DisplayLuts.h | 17 +- libs/renderengine/Android.bp | 1 + libs/renderengine/skia/SkiaRenderEngine.cpp | 4 + libs/renderengine/skia/SkiaRenderEngine.h | 2 + libs/renderengine/skia/filters/LutShader.cpp | 242 +++++++++++++++++++++ libs/renderengine/skia/filters/LutShader.h | 44 ++++ .../include/compositionengine/impl/OutputLayer.h | 2 +- .../CompositionEngine/src/OutputLayer.cpp | 16 +- .../FrontEnd/RequestedLayerState.cpp | 4 +- 13 files changed, 422 insertions(+), 15 deletions(-) create mode 100644 libs/gui/DisplayLuts.cpp create mode 100644 libs/renderengine/skia/filters/LutShader.cpp create mode 100644 libs/renderengine/skia/filters/LutShader.h (limited to 'libs') diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 80e148be9f..1e33abbdea 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -274,6 +274,7 @@ filegroup { "LayerMetadata.cpp", "LayerStatePermissions.cpp", "LayerState.cpp", + "DisplayLuts.cpp", "OccupancyTracker.cpp", "StreamSplitter.cpp", "ScreenCaptureResults.cpp", diff --git a/libs/gui/DisplayLuts.cpp b/libs/gui/DisplayLuts.cpp new file mode 100644 index 0000000000..80429765be --- /dev/null +++ b/libs/gui/DisplayLuts.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/gui/DisplayLuts.h" +#include +#include + +namespace android::gui { + +status_t DisplayLuts::Entry::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->readInt32, &dimension); + SAFE_PARCEL(parcel->readInt32, &size); + SAFE_PARCEL(parcel->readInt32, &samplingKey); + + return OK; +} + +status_t DisplayLuts::Entry::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->writeInt32, dimension); + SAFE_PARCEL(parcel->writeInt32, size); + SAFE_PARCEL(parcel->writeInt32, samplingKey); + + return OK; +} + +status_t DisplayLuts::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->readUniqueFileDescriptor, &fd); + SAFE_PARCEL(parcel->readInt32Vector, &offsets); + int32_t numLutProperties; + SAFE_PARCEL(parcel->readInt32, &numLutProperties); + lutProperties.reserve(numLutProperties); + for (int32_t i = 0; i < numLutProperties; i++) { + lutProperties.push_back({}); + SAFE_PARCEL(lutProperties.back().readFromParcel, parcel); + } + return OK; +} + +status_t DisplayLuts::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->writeUniqueFileDescriptor, fd); + SAFE_PARCEL(parcel->writeInt32Vector, offsets); + SAFE_PARCEL(parcel->writeInt32, static_cast(lutProperties.size())); + for (auto& entry : lutProperties) { + SAFE_PARCEL(entry.writeToParcel, parcel); + } + return OK; +} +} // namespace android::gui \ No newline at end of file diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 4b531345b0..139764ac0c 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -203,6 +203,12 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel); } + const bool hasLuts = (luts != nullptr); + SAFE_PARCEL(output.writeBool, hasLuts); + if (hasLuts) { + SAFE_PARCEL(output.writeParcelable, *luts); + } + return NO_ERROR; } @@ -358,6 +364,15 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get()); } + bool hasLuts; + SAFE_PARCEL(input.readBool, &hasLuts); + if (hasLuts) { + luts = std::make_shared(); + SAFE_PARCEL(input.readParcelable, luts.get()); + } else { + luts = nullptr; + } + return NO_ERROR; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 3260c53a62..807f8509b0 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1971,9 +1971,13 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts( return *this; } - s->luts = std::make_shared(base::unique_fd(dup(lutFd.get())), offsets, - dimensions, sizes, samplingKeys); s->what |= layer_state_t::eLutsChanged; + if (lutFd.ok()) { + s->luts = std::make_shared(base::unique_fd(dup(lutFd.get())), offsets, + dimensions, sizes, samplingKeys); + } else { + s->luts = nullptr; + } registerSurfaceControlForCallback(sc); return *this; diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h index 16a360dcee..ab86ac4af8 100644 --- a/libs/gui/include/gui/DisplayLuts.h +++ b/libs/gui/include/gui/DisplayLuts.h @@ -16,16 +16,24 @@ #pragma once #include +#include +#include #include namespace android::gui { -struct DisplayLuts { +struct DisplayLuts : public Parcelable { public: - struct Entry { + struct Entry : public Parcelable { + Entry() {}; + Entry(int32_t lutDimension, int32_t lutSize, int32_t lutSamplingKey) + : dimension(lutDimension), size(lutSize), samplingKey(lutSamplingKey) {} int32_t dimension; int32_t size; int32_t samplingKey; + + status_t writeToParcel(android::Parcel* parcel) const override; + status_t readFromParcel(const android::Parcel* parcel) override; }; DisplayLuts() {} @@ -42,7 +50,10 @@ public: } } - base::unique_fd& getLutFileDescriptor() { return fd; } + status_t writeToParcel(android::Parcel* parcel) const override; + status_t readFromParcel(const android::Parcel* parcel) override; + + const base::unique_fd& getLutFileDescriptor() const { return fd; } std::vector lutProperties; std::vector offsets; diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index d248ea0b84..7f207f0670 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -105,6 +105,7 @@ filegroup { "skia/filters/KawaseBlurDualFilter.cpp", "skia/filters/KawaseBlurFilter.cpp", "skia/filters/LinearEffect.cpp", + "skia/filters/LutShader.cpp", "skia/filters/MouriMap.cpp", "skia/filters/StretchShaderFactory.cpp", "skia/filters/EdgeExtensionShaderFactory.cpp", diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index ec9d3efb88..5c46c9168d 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -543,6 +543,10 @@ sk_sp SkiaRenderEngine::createRuntimeEffectShader( } } + if (graphicBuffer && parameters.layer.luts) { + shader = mLutShader.lutShader(shader, parameters.layer.luts); + } + if (parameters.requiresLinearEffect) { const auto format = targetBuffer != nullptr ? std::optional( diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index b5f8898263..7be4c253e7 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -39,6 +39,7 @@ #include "filters/BlurFilter.h" #include "filters/EdgeExtensionShaderFactory.h" #include "filters/LinearEffect.h" +#include "filters/LutShader.h" #include "filters/StretchShaderFactory.h" class SkData; @@ -184,6 +185,7 @@ private: StretchShaderFactory mStretchShaderFactory; EdgeExtensionShaderFactory mEdgeExtensionShaderFactory; + LutShader mLutShader; sp mLastDrawFence; BlurFilter* mBlurFilter = nullptr; diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp new file mode 100644 index 0000000000..cea46ef40e --- /dev/null +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -0,0 +1,242 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "LutShader.h" + +#include +#include +#include +#include +#include + +#include "include/core/SkColorSpace.h" +#include "src/core/SkColorFilterPriv.h" + +using aidl::android::hardware::graphics::composer3::LutProperties; + +namespace android { +namespace renderengine { +namespace skia { + +static const SkString kShader = SkString(R"( + uniform shader image; + uniform shader lut; + uniform int size; + uniform int key; + uniform int dimension; + vec4 main(vec2 xy) { + float4 rgba = image.eval(xy); + float3 linear = toLinearSrgb(rgba.rgb); + if (dimension == 1) { + // RGB + if (key == 0) { + float indexR = linear.r * float(size - 1); + float indexG = linear.g * float(size - 1); + float indexB = linear.b * float(size - 1); + float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r; + float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r; + float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r; + return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a); + // MAX_RGB + } else if (key == 1) { + float4 rgba = image.eval(xy); + float3 linear = toLinearSrgb(rgba.rgb); + float maxRGB = max(linear.r, max(linear.g, linear.b)); + float index = maxRGB * float(size - 1); + float gain = lut.eval(vec2(index, 0.0) + 0.5).r; + return float4(linear * gain, rgba.a); + } + } else if (dimension == 3) { + if (key == 0) { + float tx = linear.r * float(size - 1); + float ty = linear.g * float(size - 1); + float tz = linear.b * float(size - 1); + + // calculate lower and upper bounds for each dimension + int x = int(tx); + int y = int(ty); + int z = int(tz); + + int i000 = x + y * size + z * size * size; + int i100 = i000 + 1; + int i010 = i000 + size; + int i110 = i000 + size + 1; + int i001 = i000 + size * size; + int i101 = i000 + size * size + 1; + int i011 = i000 + size * size + size; + int i111 = i000 + size * size + size + 1; + + // get 1d normalized indices + float c000 = float(i000) / float(size * size * size); + float c100 = float(i100) / float(size * size * size); + float c010 = float(i010) / float(size * size * size); + float c110 = float(i110) / float(size * size * size); + float c001 = float(i001) / float(size * size * size); + float c101 = float(i101) / float(size * size * size); + float c011 = float(i011) / float(size * size * size); + float c111 = float(i111) / float(size * size * size); + + //TODO(b/377984618): support Tetrahedral interpolation + // perform trilinear interpolation + float3 c00 = mix(lut.eval(vec2(c000, 0.0) + 0.5).rgb, + lut.eval(vec2(c100, 0.0) + 0.5).rgb, linear.r); + float3 c01 = mix(lut.eval(vec2(c001, 0.0) + 0.5).rgb, + lut.eval(vec2(c101, 0.0) + 0.5).rgb, linear.r); + float3 c10 = mix(lut.eval(vec2(c010, 0.0) + 0.5).rgb, + lut.eval(vec2(c110, 0.0) + 0.5).rgb, linear.r); + float3 c11 = mix(lut.eval(vec2(c011, 0.0) + 0.5).rgb, + lut.eval(vec2(c111, 0.0) + 0.5).rgb, linear.r); + + float3 c0 = mix(c00, c10, linear.g); + float3 c1 = mix(c01, c11, linear.g); + + float3 val = mix(c0, c1, linear.b); + + return float4(val, rgba.a); + } + } + return rgba; + })"); + +sk_sp LutShader::generateLutShader(sk_sp input, + const std::vector& buffers, + const int32_t offset, const int32_t length, + const int32_t dimension, const int32_t size, + const int32_t samplingKey) { + SFTRACE_NAME("lut shader"); + std::vector buffer(length * 4); // 4 is for RGBA + auto d = static_cast(dimension); + if (d == LutProperties::Dimension::ONE_D) { + auto it = buffers.begin() + offset; + std::generate(buffer.begin(), buffer.end(), [it, i = 0]() mutable { + float val = (i++ % 4 == 0) ? *it++ : 0.0f; + return half(val); + }); + } else { + for (int i = 0; i < length; i++) { + buffer[i * 4] = half(buffers[offset + i]); + buffer[i * 4 + 1] = half(buffers[offset + length + i]); + buffer[i * 4 + 2] = half(buffers[offset + length * 2 + i]); + buffer[i * 4 + 3] = half(0); + } + } + /** + * 1D Lut(rgba) + * (R0, 0, 0, 0) + * (R1, 0, 0, 0) + * ... + * + * 3D Lut + * (R0, G0, B0, 0) + * (R1, G1, B1, 0) + * ... + */ + SkImageInfo info = SkImageInfo::Make(length /* the number of rgba */ * 4, 1, + kRGBA_F16_SkColorType, kPremul_SkAlphaType); + SkBitmap bitmap; + bitmap.allocPixels(info); + if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) { + LOG_ALWAYS_FATAL("unable to install pixels"); + } + + sk_sp lutImage = SkImages::RasterFromBitmap(bitmap); + mBuilder->child("image") = input; + mBuilder->child("lut") = + lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp, + d == LutProperties::Dimension::ONE_D + ? SkSamplingOptions(SkFilterMode::kLinear) + : SkSamplingOptions()); + + const int uSize = static_cast(size); + const int uKey = static_cast(samplingKey); + const int uDimension = static_cast(dimension); + mBuilder->uniform("size") = uSize; + mBuilder->uniform("key") = uKey; + mBuilder->uniform("dimension") = uDimension; + return mBuilder->makeShader(); +} + +sk_sp LutShader::lutShader(sk_sp& input, + std::shared_ptr displayLuts) { + if (mBuilder == nullptr) { + const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader); + mBuilder = std::make_unique(instance.effect); + } + + auto& fd = displayLuts->getLutFileDescriptor(); + if (fd.ok()) { + // de-gamma the image without changing the primaries + SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); + if (baseImage) { + sk_sp baseColorSpace = + baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); + sk_sp gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); + auto colorXformSdrToGainmap = + SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace); + input = input->makeWithColorFilter(colorXformSdrToGainmap); + } + + auto& offsets = displayLuts->offsets; + auto& lutProperties = displayLuts->lutProperties; + std::vector buffers; + int fullLength = offsets[lutProperties.size() - 1]; + if (lutProperties[lutProperties.size() - 1].dimension == 1) { + fullLength += lutProperties[lutProperties.size() - 1].size; + } else { + fullLength += (lutProperties[lutProperties.size() - 1].size * + lutProperties[lutProperties.size() - 1].size * + lutProperties[lutProperties.size() - 1].size * 3); + } + size_t bufferSize = fullLength * sizeof(float); + + // decode the shared memory of luts + float* ptr = + (float*)mmap(NULL, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0); + if (ptr == MAP_FAILED) { + LOG_ALWAYS_FATAL("mmap failed"); + } + buffers = std::vector(ptr, ptr + fullLength); + munmap(ptr, bufferSize); + + for (size_t i = 0; i < offsets.size(); i++) { + int bufferSizePerLut = (i == offsets.size() - 1) ? buffers.size() - offsets[i] + : offsets[i + 1] - offsets[i]; + // divide by 3 for 3d Lut because of 3 (RGB) channels + if (static_cast(lutProperties[i].dimension) == + LutProperties::Dimension::THREE_D) { + bufferSizePerLut /= 3; + } + input = generateLutShader(input, buffers, offsets[i], bufferSizePerLut, + lutProperties[i].dimension, lutProperties[i].size, + lutProperties[i].samplingKey); + } + + // re-gamma + baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); + if (baseImage) { + sk_sp baseColorSpace = + baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); + sk_sp gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); + auto colorXformGainmapToDst = + SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, baseColorSpace); + input = input->makeWithColorFilter(colorXformGainmapToDst); + } + } + return input; +} + +} // namespace skia +} // namespace renderengine +} // namespace android \ No newline at end of file diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h new file mode 100644 index 0000000000..c157904b63 --- /dev/null +++ b/libs/renderengine/skia/filters/LutShader.h @@ -0,0 +1,44 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *- + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include +#include + +namespace android { +namespace renderengine { +namespace skia { + +class LutShader { +public: + sk_sp lutShader(sk_sp& input, + std::shared_ptr displayLuts); + +private: + sk_sp generateLutShader(sk_sp input, const std::vector& buffers, + const int32_t offset, const int32_t length, + const int32_t dimension, const int32_t size, + const int32_t samplingKey); + std::unique_ptr mBuilder; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index 0c7e4dd071..bfa387dbfb 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -101,7 +101,7 @@ private: aidl::android::hardware::graphics::composer3::Composition from, aidl::android::hardware::graphics::composer3::Composition to) const; bool isClientCompositionForced(bool isPeekingThrough) const; - void updateLuts(std::shared_ptr luts, + void updateLuts(const LayerFECompositionState&, const std::optional>>& properties); }; diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 934909d066..c062df37e8 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -286,8 +286,13 @@ uint32_t OutputLayer::calculateOutputRelativeBufferTransform( } void OutputLayer::updateLuts( - std::shared_ptr layerFEStateLut, + const LayerFECompositionState& layerFEState, const std::optional>>& properties) { + auto& luts = layerFEState.luts; + if (!luts) { + return; + } + auto& state = editState(); if (!properties) { @@ -303,7 +308,7 @@ void OutputLayer::updateLuts( } } - for (const auto& inputLut : layerFEStateLut->lutProperties) { + for (const auto& inputLut : luts->lutProperties) { bool foundInHwcLuts = false; for (const auto& hwcLut : hwcLutProperties) { if (static_cast(hwcLut.dimension) == @@ -316,7 +321,7 @@ void OutputLayer::updateLuts( break; } } - // if any lut properties of layerFEStateLut can not be found in hwcLutProperties, + // if any lut properties of luts can not be found in hwcLutProperties, // GPU composition instead if (!foundInHwcLuts) { state.forceClientComposition = true; @@ -409,10 +414,7 @@ void OutputLayer::updateCompositionState( state.whitePointNits = layerBrightnessNits; } - const auto& layerFEStateLut = layerFEState->luts; - if (layerFEStateLut) { - updateLuts(layerFEStateLut, properties); - } + updateLuts(*layerFEState, properties); // These are evaluated every frame as they can potentially change at any // time. diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 713a5c509e..ee9302b937 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -163,7 +163,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta uint64_t clientChanges = what | layer_state_t::diff(clientState); layer_state_t::merge(clientState); what = clientChanges; - LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges); + LLOGV(layerId, "requested=%" PRIu64 " flags=%" PRIu64 " ", clientState.what, clientChanges); if (clientState.what & layer_state_t::eFlagsChanged) { if ((oldFlags ^ flags) & @@ -633,7 +633,7 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged | - layer_state_t::eDesiredHdrHeadroomChanged | + layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged | (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed() ? layer_state_t::eFlagsChanged : 0); -- cgit v1.2.3-59-g8ed1b From 94cd7bf910db555b6cc550d61d1e715b5b242d5f Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Thu, 17 Oct 2024 15:24:47 +0000 Subject: Add DISPLAY_BT2020 dataspace This already exists in AIDL, and will exist in the SDK Bug: 355264141 Flag: EXEMPT NDK can't be flagged! Test: builds Change-Id: I32fd0e9d5c58363393d5217211d1f5fe9e14f86b --- libs/nativewindow/ANativeWindow.cpp | 2 ++ libs/nativewindow/include/android/data_space.h | 7 +++++++ 2 files changed, 9 insertions(+) (limited to 'libs') diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index ac3a832168..5ce4076476 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -222,6 +222,8 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa static_cast(HAL_DATASPACE_BT2020_ITU_HLG)); static_assert(static_cast(ADATASPACE_DEPTH) == static_cast(HAL_DATASPACE_DEPTH)); static_assert(static_cast(ADATASPACE_DYNAMIC_DEPTH) == static_cast(HAL_DATASPACE_DYNAMIC_DEPTH)); + static_assert(static_cast(ADATASPACE_DISPLAY_BT2020) == + static_cast(HAL_DATASPACE_DISPLAY_BT2020)); if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { return -EINVAL; diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 8056d9ac4f..295a307c3c 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -578,6 +578,13 @@ enum ADataSpace : int32_t { */ ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | // ADATASPACE_RANGE_LIMITED + /** + * sRGB-encoded BT. 2020 + * + * Uses full range, sRGB transfer and BT2020 standard. + */ + ADATASPACE_DISPLAY_BT2020 = 142999552, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SRGB + // | ADATASPACE_RANGE_FULL /** * Depth -- cgit v1.2.3-59-g8ed1b From e524dd9d136049bbca78c2e148b5556ad3c128eb Mon Sep 17 00:00:00 2001 From: Melody Hsu Date: Tue, 27 Aug 2024 22:27:29 +0000 Subject: Recover from buffer stuffing for canned animations Buffer stuffing occurs when SurfaceFlinger misses a frame, but the client continues to produce buffers at the same rate, causing a greater risk for jank to occur. Recovery is achieved for canned animations by adjusting the animation timeline on the client side so that SurfaceFlinger is no longer behind. Use SF backdoor command 1045 to inject jank. Usage: adb shell service call SurfaceFlinger 1045 f 1 Bug: b/294922229 Test: atest EventThreadTest Test: presubmit, manually check perfetto traces Flag: android.view.flags.buffer_stuffing_recovery Change-Id: I38f0eb3d6ef1331e07d6022fa3a0e16c556ba06f --- libs/gui/BufferStuffing.md | 7 +++ libs/gui/include/gui/DisplayEventReceiver.h | 2 +- libs/gui/include/gui/LayerState.h | 4 ++ libs/gui/include/gui/VsyncEventData.h | 3 + libs/gui/tests/DisplayEventStructLayout_test.cpp | 11 ++-- .../surfaceflinger/FrameTimeline/FrameTimeline.cpp | 14 +++++ .../surfaceflinger/FrameTimeline/FrameTimeline.h | 10 ++++ services/surfaceflinger/Scheduler/EventThread.cpp | 27 ++++++++- services/surfaceflinger/Scheduler/EventThread.h | 12 +++- services/surfaceflinger/Scheduler/Scheduler.cpp | 5 ++ services/surfaceflinger/Scheduler/Scheduler.h | 5 +- services/surfaceflinger/SurfaceFlinger.cpp | 30 ++++++++++ services/surfaceflinger/SurfaceFlinger.h | 2 + .../tests/unittests/EventThreadTest.cpp | 69 ++++++++++++++++++++++ .../tests/unittests/mock/MockEventThread.h | 1 + 15 files changed, 192 insertions(+), 10 deletions(-) create mode 100644 libs/gui/BufferStuffing.md (limited to 'libs') diff --git a/libs/gui/BufferStuffing.md b/libs/gui/BufferStuffing.md new file mode 100644 index 0000000000..6ed8ad988b --- /dev/null +++ b/libs/gui/BufferStuffing.md @@ -0,0 +1,7 @@ +### Buffer Stuffing and Recovery ### + +Buffer stuffing happens on the client side when SurfaceFlinger misses a frame, but the client continues producing buffers at the same rate. This could occur anytime when SurfaceFlinger does not meet the expected timeline’s deadline to finish composing a frame. As a result, SurfaceFlinger cannot yet release the buffer for the frame that it missed and the client has one less buffer to render into. The client may then run out of buffers or have to wait for buffer release callbacks, increasing the chances of janking when clients render multiple windows. + +Recovery is implemented by first detecting when buffer stuffing occurs and ensuring that the elevated buffer counts in the server are from a relevant SurfaceControl (is a ViewRootImpl). Other SurfaceControl buffer producers such as games, media, and camera have other reasons for expectedly increased buffer counts, which do not need buffer stuffing recovery. + +The actual recovery adjusts the animation timeline in the Choreographer so that the client deadlines for subsequent frames are moved forward in time by one frame. This approach adjusts the client buffer production timeline such that SurfaceFlinger does not fall behind when it misses a frame because the client will simply match its frame production rate with SurfaceFlinger. Ordinarily, buffer stuffing is problematic because the client continues producing buffers when SurfaceFlinger is behind. However, if the client delays producing its buffers to match SurfaceFlinger’s rate, the animation has new frame deadlines that can be reasonably met. The animation is effectively paused for one frame longer than originally intended, and continues the remainder of the animation normally. \ No newline at end of file diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 4dbf9e1929..40a6e79a24 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -119,7 +119,7 @@ public: HdcpLevelsChange hdcpLevelsChange; }; }; - static_assert(sizeof(Event) == 216); + static_assert(sizeof(Event) == 224); public: /* diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 6bfeaec26a..e296e58d68 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -170,6 +170,10 @@ struct layer_state_t { // Sets a property on this layer indicating that its visible region should be considered // when computing TrustedPresentation Thresholds. eCanOccludePresentation = 0x1000, + // Indicates that the SurfaceControl should recover from buffer stuffing when + // possible. This is the case when the SurfaceControl is the root SurfaceControl + // owned by ViewRootImpl. + eRecoverableFromBufferStuffing = 0x2000, }; enum { diff --git a/libs/gui/include/gui/VsyncEventData.h b/libs/gui/include/gui/VsyncEventData.h index b40a84099c..ced5130505 100644 --- a/libs/gui/include/gui/VsyncEventData.h +++ b/libs/gui/include/gui/VsyncEventData.h @@ -36,6 +36,9 @@ struct VsyncEventData { // Size of frame timelines provided by the platform; max is kFrameTimelinesCapacity. uint32_t frameTimelinesLength; + // Number of queued buffers to indicate if buffer stuffing mode is detected. + uint32_t numberQueuedBuffers; + struct alignas(8) FrameTimeline { // The Vsync Id corresponsing to this vsync event. This will be used to // populate ISurfaceComposer::setFrameTimelineVsync and diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp index 29eeaa806f..791f471a1d 100644 --- a/libs/gui/tests/DisplayEventStructLayout_test.cpp +++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp @@ -36,15 +36,16 @@ TEST(DisplayEventStructLayoutTest, TestEventAlignment) { CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameInterval, 8); CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.preferredFrameTimelineIndex, 16); CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelinesLength, 20); - CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 24); - CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 24); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.numberQueuedBuffers, 24); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 32); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 32); CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].deadlineTimestamp, - 32); + 40); CHECK_OFFSET(DisplayEventReceiver::Event::VSync, - vsyncData.frameTimelines[0].expectedPresentationTime, 40); + vsyncData.frameTimelines[0].expectedPresentationTime, 48); // Also test the offsets of the last frame timeline. A loop is not used because the non-const // index cannot be used in static_assert. - const int lastFrameTimelineOffset = /* Start of array */ 24 + + const int lastFrameTimelineOffset = /* Start of array */ 32 + (VsyncEventData::kFrameTimelinesCapacity - 1) * /* Size of FrameTimeline */ 24; CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1].vsyncId, diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 47b811b721..e78bd9ace9 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "../Jank/JankTracker.h" @@ -997,6 +998,11 @@ void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, finalizeCurrentDisplayFrame(); } +const std::vector>& FrameTimeline::getPresentFrames() + const { + return mPresentFrames; +} + void FrameTimeline::onCommitNotComposited() { SFTRACE_CALL(); std::scoped_lock lock(mMutex); @@ -1492,6 +1498,7 @@ void FrameTimeline::flushPendingPresentFences() { mPendingPresentFences.erase(mPendingPresentFences.begin()); } + mPresentFrames.clear(); for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& pendingPresentFence = mPendingPresentFences[i]; nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; @@ -1504,6 +1511,13 @@ void FrameTimeline::flushPendingPresentFences() { auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); + + // Surface frames have been jank classified and can be provided to caller + // to detect if buffer stuffing is occurring. + for (const auto& frame : displayFrame->getSurfaceFrames()) { + mPresentFrames.push_back(frame); + } + mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index cffb61ee10..1e0940f7a6 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -323,6 +323,11 @@ public: virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr& presentFence, const std::shared_ptr& gpuFence) = 0; + // Provides surface frames that have already been jank classified in the most recent + // flush of pending present fences. This allows buffer stuffing detection from SF. + virtual const std::vector>& getPresentFrames() + const = 0; + // Tells FrameTimeline that a frame was committed but not composited. This is used to flush // all the associated surface frames. virtual void onCommitNotComposited() = 0; @@ -497,6 +502,8 @@ public: void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr& presentFence, const std::shared_ptr& gpuFence = FenceTime::NO_FENCE) override; + const std::vector>& getPresentFrames() + const override; void onCommitNotComposited() override; void parseArgs(const Vector& args, std::string& result) override; void setMaxDisplayFrames(uint32_t size) override; @@ -543,6 +550,9 @@ private: // display frame, this is a good starting size for the vector so that we can avoid the // internal vector resizing that happens with push_back. static constexpr uint32_t kNumSurfaceFramesInitial = 10; + // Presented surface frames that have been jank classified and can + // indicate of potential buffer stuffing. + std::vector> mPresentFrames; }; } // namespace impl diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index e385f18243..24d387a498 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -44,10 +44,8 @@ #include #include -#include "DisplayHardware/DisplayMode.h" #include "FrameTimeline.h" #include "VSyncDispatch.h" -#include "VSyncTracker.h" #include "EventThread.h" @@ -472,6 +470,14 @@ void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t conne mCondition.notify_all(); } +// Merge lists of buffer stuffed Uids +void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { + std::lock_guard lock(mMutex); + for (auto& [uid, count] : bufferStuffedUids) { + mBufferStuffedUids.emplace_or_replace(uid, count); + } +} + void EventThread::threadMain(std::unique_lock& lock) { DisplayEventConsumers consumers; @@ -701,6 +707,10 @@ void EventThread::generateFrameTimeline(VsyncEventData& outVsyncEventData, nsecs void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, const DisplayEventConsumers& consumers) { + // List of Uids that have been sent vsync data with queued buffer count. + // Used to keep track of which Uids can be removed from the map of + // buffer stuffed clients. + ftl::SmallVector uidsPostedQueuedBuffers; for (const auto& consumer : consumers) { DisplayEventReceiver::Event copy = event; if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { @@ -710,6 +720,13 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, event.vsync.vsyncData.preferredExpectedPresentationTime(), event.vsync.vsyncData.preferredDeadlineTimestamp()); } + auto it = mBufferStuffedUids.find(consumer->mOwnerUid); + if (it != mBufferStuffedUids.end()) { + copy.vsync.vsyncData.numberQueuedBuffers = it->second; + uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid); + } else { + copy.vsync.vsyncData.numberQueuedBuffers = 0; + } switch (consumer->postEvent(copy)) { case NO_ERROR: break; @@ -725,6 +742,12 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, removeDisplayEventConnectionLocked(consumer); } } + // The clients that have already received the queued buffer count + // can be removed from the buffer stuffed Uid list to avoid + // being sent duplicate messages. + for (auto uid : uidsPostedQueuedBuffers) { + mBufferStuffedUids.erase(uid); + } if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC && FlagManager::getInstance().vrr_config()) { mLastCommittedVsyncTime = diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index c3c7eb0ee1..02e2592914 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -32,7 +32,6 @@ #include #include -#include "DisplayHardware/DisplayMode.h" #include "TracedOrdinal.h" #include "VSyncDispatch.h" #include "VsyncSchedule.h" @@ -55,6 +54,7 @@ using gui::VsyncEventData; // --------------------------------------------------------------------------- using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; +using BufferStuffingMap = ftl::SmallMap; enum class VSyncRequest { None = -2, @@ -134,6 +134,10 @@ public: virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) = 0; + + // An elevated number of queued buffers in the server is detected. This propagates a + // flag to Choreographer indicating that buffer stuffing recovery should begin. + virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); }; struct IEventThreadCallback { @@ -184,6 +188,8 @@ public: void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) override; + void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override; + private: friend EventThreadTest; @@ -224,6 +230,10 @@ private: scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; + // All consumers that need to recover from buffer stuffing and the number + // of their queued buffers. + BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex); + IEventThreadCallback& mCallback; std::thread mThread; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index b83ff19fe7..b984e92f20 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -940,6 +940,11 @@ bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals, return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); } +void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { + if (!mRenderEventThread) return; + mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids)); +} + void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) { std::shared_ptr pacesetterVsyncSchedule; { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index c88b563805..184242640c 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -42,7 +42,6 @@ #include #include -#include "Display/DisplayModeRequest.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" #include "ISchedulerCallback.h" @@ -332,6 +331,10 @@ public: mPacesetterFrameDurationFractionToSkip = frameDurationFraction; } + // Propagates a flag to the EventThread indicating that buffer stuffing + // recovery should begin. + void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); + private: friend class TestableScheduler; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index d35a76ad4b..c0eade840b 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -64,11 +64,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -3066,12 +3068,40 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const TimePoint presentTime = TimePoint::now(); + // The Uids of layer owners that are in buffer stuffing mode, and their elevated + // buffer counts. Messages to start recovery are sent exclusively to these Uids. + BufferStuffingMap bufferStuffedUids; + // Set presentation information before calling Layer::releasePendingBuffer, such that jank // information from previous' frame classification is already available when sending jank info // to clients, so they get jank classification as early as possible. mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime, pacesetterGpuCompositionDoneFenceTime); + // Find and register any layers that are in buffer stuffing mode + const auto& presentFrames = mFrameTimeline->getPresentFrames(); + + for (const auto& frame : presentFrames) { + const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId()); + if (!layer) continue; + uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0; + int32_t jankType = frame->getJankType().value_or(JankType::None); + if (jankType & JankType::BufferStuffing && + layer->flags & layer_state_t::eRecoverableFromBufferStuffing) { + auto [it, wasEmplaced] = + bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers); + // Update with maximum number of queued buffers, allows clients drawing + // multiple windows to account for the most severely stuffed window + if (!wasEmplaced && it->second < numberQueuedBuffers) { + it->second = numberQueuedBuffers; + } + } + } + + if (!bufferStuffedUids.empty()) { + mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids)); + } + // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might // be sampled a little later than when we started doing work for this frame, // but that should be okay since CompositorTiming has snapping logic. diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 31218edbe7..5d83315fcc 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1382,6 +1382,8 @@ private: // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; + // Tracks the number of maximum queued buffers by layer owner Uid. + using BufferStuffingMap = ftl::SmallMap; BufferCountTracker mBufferCountTracker; std::unordered_map> mHdrLayerInfoListeners diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 625d2e68d8..268a6c416d 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -111,6 +112,8 @@ protected: void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime); void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector); + void expectQueuedBufferCountReceivedByConnection( + ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount); void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime, nsecs_t deadlineTimestamp) { @@ -144,6 +147,7 @@ protected: sp mConnection; sp mThrottledConnection; std::unique_ptr mTokenManager; + std::vector mBufferStuffedConnectionRecorders; std::chrono::nanoseconds mVsyncPeriod; @@ -376,6 +380,14 @@ void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection( EXPECT_EQ(expectedDisplayId, event.header.displayId); } +void EventThreadTest::expectQueuedBufferCountReceivedByConnection( + ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) { + auto args = connectionEventRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers); +} + namespace { using namespace testing; @@ -868,6 +880,63 @@ TEST_F(EventThreadTest, postHcpLevelsChanged) { EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel); } +TEST_F(EventThreadTest, connectionReceivesBufferStuffing) { + setupEventThread(); + + // Create a connection that will experience buffer stuffing. + ConnectionEventRecorder stuffedConnectionEventRecorder{0}; + sp stuffedConnection = + createConnection(stuffedConnectionEventRecorder, + gui::ISurfaceComposer::EventRegistration::modeChanged | + gui::ISurfaceComposer::EventRegistration::frameRateOverride, + 111); + + // Add a connection and buffer count to the list of stuffed Uids that will receive + // data in the next vsync event. + BufferStuffingMap bufferStuffedUids; + bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3); + mThread->addBufferStuffedUids(bufferStuffedUids); + mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder); + + // Signal that we want the next vsync event to be posted to two connections. + mThread->requestNextVsync(mConnection); + mThread->requestNextVsync(stuffedConnection); + onVSyncEvent(123, 456, 789); + + // Vsync event data contains number of queued buffers. + expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0); + expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3); +} + +TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) { + setupEventThread(); + + // Create a connection with the same Uid as another connection. + ConnectionEventRecorder secondConnectionEventRecorder{0}; + sp secondConnection = + createConnection(secondConnectionEventRecorder, + gui::ISurfaceComposer::EventRegistration::modeChanged | + gui::ISurfaceComposer::EventRegistration::frameRateOverride, + mConnectionUid); + + // Add connection Uid and buffer count to the list of stuffed Uids that will receive + // data in the next vsync event. + BufferStuffingMap bufferStuffedUids; + bufferStuffedUids.try_emplace(mConnectionUid, 3); + mThread->addBufferStuffedUids(bufferStuffedUids); + mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder); + mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder); + + // Signal that we want the next vsync event to be posted to two connections. + mThread->requestNextVsync(mConnection); + mThread->requestNextVsync(secondConnection); + onVSyncEvent(123, 456, 789); + + // Vsync event data contains number of queued buffers. + expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3); + expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 7398cbebe3..c49bd300c0 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -52,6 +52,7 @@ public: MOCK_METHOD(void, onHdcpLevelsChanged, (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel), (override)); + MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override)); }; } // namespace android::mock -- cgit v1.2.3-59-g8ed1b From b658b15cd4f694384a2627834bbfb62b323df5a9 Mon Sep 17 00:00:00 2001 From: Sally Qi Date: Mon, 11 Nov 2024 19:43:42 -0800 Subject: [Lut shader] Fix gamma correction to avoid color conversion, we de-gamma the image without changing the primaries, after lut(s) shader being applied, we need to re-gamma it back. Bug: 329472856 Test: luts cts cases Flag: android.hardware.flags.luts_api Change-Id: I8c8a3b6d445d11efab4115c097d7eeb91d1be384 --- libs/renderengine/skia/SkiaRenderEngine.cpp | 3 ++- libs/renderengine/skia/filters/LutShader.cpp | 29 ++++++++++------------------ libs/renderengine/skia/filters/LutShader.h | 4 ++-- 3 files changed, 14 insertions(+), 22 deletions(-) (limited to 'libs') diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 5c46c9168d..a93f6c36ff 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -544,7 +544,8 @@ sk_sp SkiaRenderEngine::createRuntimeEffectShader( } if (graphicBuffer && parameters.layer.luts) { - shader = mLutShader.lutShader(shader, parameters.layer.luts); + shader = mLutShader.lutShader(shader, parameters.layer.luts, + toSkColorSpace(parameters.outputDataSpace)); } if (parameters.requiresLinearEffect) { diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp index cea46ef40e..1e43ff3cd3 100644 --- a/libs/renderengine/skia/filters/LutShader.cpp +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -169,7 +169,8 @@ sk_sp LutShader::generateLutShader(sk_sp input, } sk_sp LutShader::lutShader(sk_sp& input, - std::shared_ptr displayLuts) { + std::shared_ptr displayLuts, + sk_sp outColorSpace) { if (mBuilder == nullptr) { const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader); mBuilder = std::make_unique(instance.effect); @@ -179,14 +180,11 @@ sk_sp LutShader::lutShader(sk_sp& input, if (fd.ok()) { // de-gamma the image without changing the primaries SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); - if (baseImage) { - sk_sp baseColorSpace = - baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); - sk_sp gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); - auto colorXformSdrToGainmap = - SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace); - input = input->makeWithColorFilter(colorXformSdrToGainmap); - } + sk_sp baseColorSpace = baseImage && baseImage->colorSpace() + ? baseImage->refColorSpace() + : SkColorSpace::MakeSRGB(); + sk_sp lutMathColorSpace = baseColorSpace->makeLinearGamma(); + input = input->makeWithWorkingColorSpace(lutMathColorSpace); auto& offsets = displayLuts->offsets; auto& lutProperties = displayLuts->lutProperties; @@ -223,16 +221,9 @@ sk_sp LutShader::lutShader(sk_sp& input, lutProperties[i].samplingKey); } - // re-gamma - baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); - if (baseImage) { - sk_sp baseColorSpace = - baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); - sk_sp gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); - auto colorXformGainmapToDst = - SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, baseColorSpace); - input = input->makeWithColorFilter(colorXformGainmapToDst); - } + auto colorXformLutToDst = + SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace); + input = input->makeWithColorFilter(colorXformLutToDst); } return input; } diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h index c157904b63..ce3e0592a1 100644 --- a/libs/renderengine/skia/filters/LutShader.h +++ b/libs/renderengine/skia/filters/LutShader.h @@ -28,8 +28,8 @@ namespace skia { class LutShader { public: - sk_sp lutShader(sk_sp& input, - std::shared_ptr displayLuts); + sk_sp lutShader(sk_sp& input, std::shared_ptr displayLuts, + sk_sp outColorSpace); private: sk_sp generateLutShader(sk_sp input, const std::vector& buffers, -- cgit v1.2.3-59-g8ed1b From 2e3bb3779dc3e23fe20de093163dc2ba016a11fa Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Wed, 13 Nov 2024 23:15:50 +0000 Subject: libgui: Add bq_gl_fence_cleanup flag Bug: 339705065 Flag: EXEMPT new flag Test: n/a Change-Id: I06cf8bce5af75cc9eb9c462e953cf13a6bfde7c1 --- libs/gui/libgui_flags.aconfig | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'libs') diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 22d32e9769..6bf38c05f1 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -131,3 +131,11 @@ flag { bug: "359252619" is_fixed_read_only: true } # bq_producer_throttles_only_async_mode + +flag { + name: "bq_gl_fence_cleanup" + namespace: "core_graphics" + description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks" + bug: "339705065" + is_fixed_read_only: true +} # bq_gl_fence_cleanup -- cgit v1.2.3-59-g8ed1b From 5210808d779bea34fcd28b4cb13888f975e71ca7 Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Fri, 15 Nov 2024 16:53:57 +0000 Subject: bufferqueues: Simplify calls that don't use GL fences We wanna get rid of this API, and we can simplify all these calls to just avoid the argument. Bug: 339705065 Flag: EXEMPT refactor Test: old tests Change-Id: I4e94e66003cdcdc197254435e5a815dd53e67a20 --- libs/gui/BufferItemConsumer.cpp | 2 +- libs/gui/GLConsumer.cpp | 14 +++---- libs/gui/IGraphicBufferConsumer.cpp | 10 +++-- libs/gui/StreamSplitter.cpp | 3 +- libs/gui/include/gui/GLConsumer.h | 6 +-- libs/gui/include/gui/IGraphicBufferConsumer.h | 9 +---- libs/gui/tests/BufferQueue_test.cpp | 45 ++++++++-------------- libs/gui/tests/StreamSplitter_test.cpp | 9 ++--- libs/gui/tests/Surface_test.cpp | 6 +-- .../include/surfacetexture/SurfaceTexture.h | 3 +- libs/nativedisplay/surfacetexture/EGLConsumer.cpp | 12 +++--- .../nativedisplay/surfacetexture/ImageConsumer.cpp | 9 ++--- 12 files changed, 50 insertions(+), 78 deletions(-) (limited to 'libs') diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index bfe3d6e023..8566419435 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -140,7 +140,7 @@ status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp #include +#include namespace android { @@ -84,7 +85,8 @@ public: EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)), const sp& releaseFence) override { - return callRemote(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); + using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp&); + return callRemote(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); } status_t consumerConnect(const sp& consumer, bool controlledByApp) override { @@ -188,8 +190,10 @@ status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer); case Tag::ATTACH_BUFFER: return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer); - case Tag::RELEASE_BUFFER: - return callLocal(data, reply, &IGraphicBufferConsumer::releaseHelper); + case Tag::RELEASE_BUFFER: { + using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp&); + return callLocal(data, reply, &IGraphicBufferConsumer::releaseBuffer); + } case Tag::CONSUMER_CONNECT: return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect); case Tag::CONSUMER_DISCONNECT: diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp index 2f8e104ea0..653b91bcf6 100644 --- a/libs/gui/StreamSplitter.cpp +++ b/libs/gui/StreamSplitter.cpp @@ -234,8 +234,7 @@ void StreamSplitter::onBufferReleasedByOutput( LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "attaching buffer to input failed (%d)", status); - status = mInput->releaseBuffer(consumerSlot, /* frameNumber */ 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker->getMergedFence()); + status = mInput->releaseBuffer(consumerSlot, /* frameNumber */ 0, tracker->getMergedFence()); LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "releasing buffer to input failed (%d)", status); diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index bfe3eb31e8..8a66dc0cf1 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -268,9 +268,9 @@ protected: // releaseBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase. - virtual status_t releaseBufferLocked(int slot, - const sp graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) override; + virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, + EGLDisplay display = EGL_NO_DISPLAY, + EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override; status_t releaseBufferLocked(int slot, const sp graphicBuffer, EGLSyncKHR eglFence) { diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h index 0b92e7df62..18f5488173 100644 --- a/libs/gui/include/gui/IGraphicBufferConsumer.h +++ b/libs/gui/include/gui/IGraphicBufferConsumer.h @@ -137,16 +137,9 @@ public: virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence, const sp& releaseFence) = 0; - status_t releaseHelper(int buf, uint64_t frameNumber, const sp& releaseFence) { + status_t releaseBuffer(int buf, uint64_t frameNumber, const sp& releaseFence) { return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); } - // This is explicitly *not* the actual signature of IGBC::releaseBuffer, but: - // 1) We have no easy way to send the EGL objects across Binder - // 2) This has always been broken, probably because - // 3) IGBC is rarely remoted - // For now, we will choose to bury our heads in the sand and ignore this problem until such time - // as we can finally finish converting away from EGL sync to native Android sync - using ReleaseBuffer = decltype(&IGraphicBufferConsumer::releaseHelper); // consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected, // and when that consumer disconnects the BufferQueue is placed into the "abandoned" state, diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index b026e640aa..16060990bd 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -425,8 +425,7 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, nullptr)); ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, Fence::NO_FENCE)); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -609,8 +608,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { ASSERT_EQ(true, item.mQueuedBuffer); ASSERT_EQ(false, item.mAutoRefresh); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // attempt to acquire a second time should return no buffer available ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE, @@ -653,8 +651,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { ASSERT_EQ(i == 0, item.mQueuedBuffer); ASSERT_EQ(true, item.mAutoRefresh); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); } // Repeatedly queue and dequeue a buffer from the producer side, it should @@ -684,8 +681,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { ASSERT_EQ(i == 0, item.mQueuedBuffer); ASSERT_EQ(true, item.mAutoRefresh); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); } } @@ -735,8 +731,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { ASSERT_EQ(true, item.mQueuedBuffer); ASSERT_EQ(false, item.mAutoRefresh); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // attempt to acquire a second time should return no buffer available ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE, @@ -874,8 +869,7 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { for (size_t i = 0; i < 2; ++i) { BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); } // Make sure we got the second buffer back @@ -929,8 +923,7 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } @@ -946,8 +939,7 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } ASSERT_EQ(OK, @@ -959,12 +951,10 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // Sleep between segments std::this_thread::sleep_for(500ms); @@ -981,13 +971,11 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // Now we read the segments std::vector history; @@ -1108,8 +1096,7 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { // Acquire and free 1 buffer ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); int releasedSlot = item.mSlot; // Acquire 1 buffer, leaving 1 filled buffer in queue @@ -1376,8 +1363,7 @@ TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(slot, item.mSlot); ASSERT_NE(nullptr, item.mGraphicBuffer.get()); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // Dequeue and queue the buffer again ASSERT_EQ(OK, @@ -1390,8 +1376,7 @@ TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(slot, item.mSlot); ASSERT_EQ(nullptr, item.mGraphicBuffer.get()); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // Dequeue and queue the buffer again ASSERT_EQ(OK, diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp index f34b03eade..1c439cdb45 100644 --- a/libs/gui/tests/StreamSplitter_test.cpp +++ b/libs/gui/tests/StreamSplitter_test.cpp @@ -95,8 +95,7 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); - ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // This should succeed even with allocation disabled since it will have // received the buffer back from the output BufferQueue @@ -168,9 +167,9 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); - ASSERT_EQ(OK, outputConsumers[output]->releaseBuffer(item.mSlot, - item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, - Fence::NO_FENCE)); + ASSERT_EQ(OK, + outputConsumers[output]->releaseBuffer(item.mSlot, item.mFrameNumber, + Fence::NO_FENCE)); } // This should succeed even with allocation disabled since it will have diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 88893b64ba..c74186682e 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -201,9 +201,9 @@ protected: releasedItems.resize(1+extraDiscardedBuffers); for (size_t i = 0; i < releasedItems.size(); i++) { ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0)); - ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot, - releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, - Fence::NO_FENCE)); + ASSERT_EQ(NO_ERROR, + consumer->releaseBuffer(releasedItems[i].mSlot, releasedItems[i].mFrameNumber, + Fence::NO_FENCE)); } int32_t expectedReleaseCb = (enableReleasedCb ? releasedItems.size() : 0); if (hasSurfaceListener) { diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index f1453bd64d..006a785cb7 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -344,7 +344,8 @@ protected: * mEglSlots array in addition to the ConsumerBase. */ virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) override; + EGLDisplay display = EGL_NO_DISPLAY, + EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override; /** * freeBufferLocked frees up the given buffer slot. If the slot has been diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 275b7a4888..3959fce008 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -150,8 +150,7 @@ status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { } } - err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); + err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer); if (err < NO_ERROR) { EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); return err; @@ -234,14 +233,14 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { EGC_LOGE("updateAndRelease: EGLConsumer is not attached to an OpenGL " "ES context"); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return INVALID_OPERATION; } // Confirm state. err = checkAndUpdateEglStateLocked(st); if (err != NO_ERROR) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return err; } @@ -254,7 +253,7 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele if (err != NO_ERROR) { EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, slot); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return UNKNOWN_ERROR; } @@ -266,8 +265,7 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele // release the old buffer, so instead we just drop the new frame. // As we are still under lock since acquireBuffer, it is safe to // release by slot. - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return err; } } diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index 32b229d77c..60e87b54d5 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -64,8 +64,7 @@ sp ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace // Wait on the producer fence for the buffer to be ready. err = fenceWait(item.mFence->get(), fencePassThroughHandle); if (err != OK) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return nullptr; } } @@ -79,8 +78,7 @@ sp ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace err = createFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), &display, &releaseFenceId, fencePassThroughHandle); if (OK != err) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return nullptr; } @@ -91,8 +89,7 @@ sp ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace releaseFence); if (err != OK) { IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return nullptr; } } -- cgit v1.2.3-59-g8ed1b From 8047b3cd1aae93f48b87e47c8b6d44f114579d05 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 15 Nov 2024 16:01:41 -0800 Subject: Use matcher-based consumption in InputPublisherAndConsumerNoResampling_test This makes it simpler to consume events in this test, and follows the existing approach in other pieces of our testing code. Bug: 305165753 Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST Flag: EXEMPT refactor Change-Id: I8481789c8ef22ddb995f1ccc800e536da7362125 --- .../InputPublisherAndConsumerNoResampling_test.cpp | 39 ++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'libs') diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index 1210f711de..39bb841c0a 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -14,15 +14,18 @@ * limitations under the License. */ +#include #include #include #include +#include #include #include +#include +#include #include #include @@ -194,6 +197,21 @@ std::optional parseEdid(const DisplayIdentificationData& edid) { const uint16_t productId = static_cast(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8)); + // Bytes 12-15: display serial number, in little-endian (LSB). This field is + // optional and its absence is marked by having all bytes set to 0x00. + // Values do not represent ASCII characters. + constexpr size_t kSerialNumberOffset = 12; + if (edid.size() < kSerialNumberOffset + sizeof(uint32_t)) { + ALOGE("Invalid EDID: block zero S/N is truncated."); + return {}; + } + const uint32_t blockZeroSerialNumber = edid[kSerialNumberOffset] + + (edid[kSerialNumberOffset + 1] << 8) + (edid[kSerialNumberOffset + 2] << 16) + + (edid[kSerialNumberOffset + 3] << 24); + const auto hashedBlockZeroSNOpt = blockZeroSerialNumber == 0 + ? std::nullopt + : ftl::stable_hash(std::string_view(std::to_string(blockZeroSerialNumber))); + constexpr size_t kManufactureWeekOffset = 16; if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) { ALOGE("Invalid EDID: manufacture week is truncated."); @@ -350,6 +368,7 @@ std::optional parseEdid(const DisplayIdentificationData& edid) { return Edid{ .manufacturerId = manufacturerId, .productId = productId, + .hashedBlockZeroSerialNumberOpt = hashedBlockZeroSNOpt, .pnpId = *pnpId, .modelHash = modelHash, .displayName = displayName, diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index 3f938d6c96..85e5cee490 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -69,6 +69,7 @@ struct Cea861ExtensionBlock : ExtensionBlock { struct Edid { uint16_t manufacturerId; uint16_t productId; + std::optional hashedBlockZeroSerialNumberOpt; PnpId pnpId; uint32_t modelHash; std::string_view displayName; diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp index cf0e98b0c6..fbbe719be8 100644 --- a/libs/ui/tests/DisplayIdentification_test.cpp +++ b/libs/ui/tests/DisplayIdentification_test.cpp @@ -33,7 +33,7 @@ namespace android { namespace { const unsigned char kInternalEdid[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00" + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x4e\x61\xbc\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" @@ -54,7 +54,7 @@ const unsigned char kExternalEdid[] = // 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" + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\xb1\x7f\x39\x05" "\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" @@ -112,7 +112,7 @@ const unsigned char kHisenseTvEdid[] = "\x07"; const unsigned char kCtlDisplayEdid[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x00\x00\x00\x00" + "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x30\x41\xab\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" @@ -191,6 +191,8 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("121AT11-801"), 626564263); EXPECT_TRUE(edid->displayName.empty()); EXPECT_EQ(12610, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("12345678"), edid->hashedBlockZeroSerialNumberOpt.value()); EXPECT_EQ(21, edid->manufactureOrModelYear); EXPECT_EQ(0, edid->manufactureWeek); EXPECT_EQ(26, edid->physicalSizeInCm.width); @@ -209,6 +211,8 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("HP ZR30w"), 918492362); EXPECT_EQ("HP ZR30w", edid->displayName); EXPECT_EQ(10348, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("16843009"), edid->hashedBlockZeroSerialNumberOpt.value()); EXPECT_EQ(22, edid->manufactureOrModelYear); EXPECT_EQ(2, edid->manufactureWeek); EXPECT_EQ(64, edid->physicalSizeInCm.width); @@ -227,6 +231,8 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("SAMSUNG"), 1201368132); EXPECT_EQ("SAMSUNG", edid->displayName); EXPECT_EQ(2302, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("87654321"), edid->hashedBlockZeroSerialNumberOpt.value()); EXPECT_EQ(21, edid->manufactureOrModelYear); EXPECT_EQ(41, edid->manufactureWeek); EXPECT_EQ(16, edid->physicalSizeInCm.width); @@ -251,6 +257,8 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("Panasonic-TV"), 3876373262); EXPECT_EQ("Panasonic-TV", edid->displayName); EXPECT_EQ(41622, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("16843009"), edid->hashedBlockZeroSerialNumberOpt.value()); EXPECT_EQ(29, edid->manufactureOrModelYear); EXPECT_EQ(0, edid->manufactureWeek); EXPECT_EQ(128, edid->physicalSizeInCm.width); @@ -275,6 +283,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("Hisense"), 2859844809); EXPECT_EQ("Hisense", edid->displayName); EXPECT_EQ(0, edid->productId); + EXPECT_FALSE(edid->hashedBlockZeroSerialNumberOpt.has_value()); EXPECT_EQ(29, edid->manufactureOrModelYear); EXPECT_EQ(18, edid->manufactureWeek); EXPECT_EQ(0, edid->physicalSizeInCm.width); @@ -299,6 +308,8 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("LP2361"), 1523181158); EXPECT_EQ("LP2361", edid->displayName); EXPECT_EQ(9373, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("11223344"), edid->hashedBlockZeroSerialNumberOpt.value()); EXPECT_EQ(23, edid->manufactureOrModelYear); EXPECT_EQ(0xff, edid->manufactureWeek); EXPECT_EQ(52, edid->physicalSizeInCm.width); -- cgit v1.2.3-59-g8ed1b From f9854d272c816ca2ad2fec085e17c6591721bff9 Mon Sep 17 00:00:00 2001 From: Gil Dekel Date: Thu, 14 Nov 2024 14:14:25 -0500 Subject: SF: Hash and cache descriptor block serial number More EDID fields are required as a part of migrating to EDID-based display IDs. This CL parses the device's serial number from the serial number descriptor bloc, (depicted by the FF marker), hashes it using a stable hash, and serves it as a part of the Edid struct. Later, amongst others, this value will be used to fabricate a unique display ID that is based on the display's EDID. See: 1. EDID spec: https://glenwing.github.io/docs/VESA-EEDID-A2.pdf 2. https://en.wikipedia.org/wiki/Extended_Display_Identification_Data#Structure,_version_1.4 Flag: com.android.graphics.surfaceflinger.flags.stable_edid_ids Bug: 378923759 Test: DisplayIdentification_test Change-Id: If9c9358b0d0850337496740d15419fd0ed330a02 --- libs/ui/DisplayIdentification.cpp | 11 ++++++++--- libs/ui/include/ui/DisplayIdentification.h | 1 + libs/ui/tests/DisplayIdentification_test.cpp | 7 +++++++ 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index 5cdaa71627..8d6f74b605 100644 --- a/libs/ui/DisplayIdentification.cpp +++ b/libs/ui/DisplayIdentification.cpp @@ -249,7 +249,8 @@ std::optional parseEdid(const DisplayIdentificationData& edid) { view = view.subspan(kDescriptorOffset); std::string_view displayName; - std::string_view serialNumber; + std::string_view descriptorBlockSerialNumber; + std::optional hashedDescriptorBlockSNOpt = std::nullopt; std::string_view asciiText; ui::Size preferredDTDPixelSize; ui::Size preferredDTDPhysicalSize; @@ -274,7 +275,10 @@ std::optional parseEdid(const DisplayIdentificationData& edid) { asciiText = parseEdidText(descriptor); break; case 0xff: - serialNumber = parseEdidText(descriptor); + descriptorBlockSerialNumber = parseEdidText(descriptor); + hashedDescriptorBlockSNOpt = descriptorBlockSerialNumber.empty() + ? std::nullopt + : ftl::stable_hash(descriptorBlockSerialNumber); break; } } else if (isDetailedTimingDescriptor(view)) { @@ -315,7 +319,7 @@ std::optional parseEdid(const DisplayIdentificationData& edid) { if (modelString.empty()) { ALOGW("Invalid EDID: falling back to serial number due to missing display name."); - modelString = serialNumber; + modelString = descriptorBlockSerialNumber; } if (modelString.empty()) { ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number."); @@ -369,6 +373,7 @@ std::optional parseEdid(const DisplayIdentificationData& edid) { .manufacturerId = manufacturerId, .productId = productId, .hashedBlockZeroSerialNumberOpt = hashedBlockZeroSNOpt, + .hashedDescriptorBlockSerialNumberOpt = hashedDescriptorBlockSNOpt, .pnpId = *pnpId, .modelHash = modelHash, .displayName = displayName, diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index 85e5cee490..cf67d7bf93 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -70,6 +70,7 @@ struct Edid { uint16_t manufacturerId; uint16_t productId; std::optional hashedBlockZeroSerialNumberOpt; + std::optional hashedDescriptorBlockSerialNumberOpt; PnpId pnpId; uint32_t modelHash; std::string_view displayName; diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp index fbbe719be8..d1699e79b6 100644 --- a/libs/ui/tests/DisplayIdentification_test.cpp +++ b/libs/ui/tests/DisplayIdentification_test.cpp @@ -193,6 +193,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(12610, edid->productId); EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); EXPECT_EQ(ftl::stable_hash("12345678"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(21, edid->manufactureOrModelYear); EXPECT_EQ(0, edid->manufactureWeek); EXPECT_EQ(26, edid->physicalSizeInCm.width); @@ -213,6 +214,8 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(10348, edid->productId); EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); EXPECT_EQ(ftl::stable_hash("16843009"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_TRUE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("CN4202137Q"), edid->hashedDescriptorBlockSerialNumberOpt.value()); EXPECT_EQ(22, edid->manufactureOrModelYear); EXPECT_EQ(2, edid->manufactureWeek); EXPECT_EQ(64, edid->physicalSizeInCm.width); @@ -233,6 +236,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(2302, edid->productId); EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); EXPECT_EQ(ftl::stable_hash("87654321"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(21, edid->manufactureOrModelYear); EXPECT_EQ(41, edid->manufactureWeek); EXPECT_EQ(16, edid->physicalSizeInCm.width); @@ -259,6 +263,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(41622, edid->productId); EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); EXPECT_EQ(ftl::stable_hash("16843009"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(29, edid->manufactureOrModelYear); EXPECT_EQ(0, edid->manufactureWeek); EXPECT_EQ(128, edid->physicalSizeInCm.width); @@ -284,6 +289,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ("Hisense", edid->displayName); EXPECT_EQ(0, edid->productId); EXPECT_FALSE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(29, edid->manufactureOrModelYear); EXPECT_EQ(18, edid->manufactureWeek); EXPECT_EQ(0, edid->physicalSizeInCm.width); @@ -310,6 +316,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(9373, edid->productId); EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); EXPECT_EQ(ftl::stable_hash("11223344"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(23, edid->manufactureOrModelYear); EXPECT_EQ(0xff, edid->manufactureWeek); EXPECT_EQ(52, edid->physicalSizeInCm.width); -- cgit v1.2.3-59-g8ed1b From e13230c11690d309826f6a76592a5b43b2c92836 Mon Sep 17 00:00:00 2001 From: Vaibhav Devmurari Date: Mon, 18 Nov 2024 15:26:32 +0000 Subject: Shift error log to info for rust data store When data store is first added, the file will be missing, so info log is more appropriate Test: manual Bug: 373465427 Flag: EXEMPT bugfix Change-Id: I9082ae1ea28b4dc21dae5ea45aef177210426240 --- libs/input/rust/data_store.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/input/rust/data_store.rs b/libs/input/rust/data_store.rs index 6bdcefda36..beb6e23478 100644 --- a/libs/input/rust/data_store.rs +++ b/libs/input/rust/data_store.rs @@ -17,7 +17,7 @@ //! Contains the DataStore, used to store input related data in a persistent way. use crate::input::KeyboardType; -use log::{debug, error}; +use log::{debug, error, info}; use serde::{Deserialize, Serialize}; use std::fs::File; use std::io::{Read, Write}; @@ -157,7 +157,7 @@ impl FileReaderWriter for DefaultFileReaderWriter { let path = Path::new(&self.filepath); let mut fs_string = String::new(); match File::open(path) { - Err(e) => error!("couldn't open {:?}: {}", path, e), + Err(e) => info!("couldn't open {:?}: {}", path, e), Ok(mut file) => match file.read_to_string(&mut fs_string) { Err(e) => error!("Couldn't read from {:?}: {}", path, e), Ok(_) => debug!("Successfully read from file {:?}", path), -- cgit v1.2.3-59-g8ed1b From 8face53ab54e08cb5fb155f3a531818404be9295 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Sat, 9 Nov 2024 22:15:32 +0000 Subject: Limit HLG to 4.926x SDR HLG is universally too bright on basically every single platform that supports HDR. Fix this for Android. Bug: 362510107 Flag: com.android.graphics.surfaceflinger.flags.begone_bright_hlg Test: HLG playback Change-Id: I5d464c016be62b11f6a3cc1ab228e14d198afb15 --- libs/ui/include_types/ui/HdrRenderTypeUtils.h | 22 +++++++++++++++++++++- .../CompositionEngine/src/OutputLayer.cpp | 18 +++++++++++++++--- services/surfaceflinger/SurfaceFlinger.cpp | 10 +++++++++- services/surfaceflinger/common/FlagManager.cpp | 2 ++ .../common/include/common/FlagManager.h | 1 + .../surfaceflinger_flags_new.aconfig | 8 ++++++++ 6 files changed, 56 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h index b0af878cdb..70c50f07e5 100644 --- a/libs/ui/include_types/ui/HdrRenderTypeUtils.h +++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h @@ -61,4 +61,24 @@ inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace, return HdrRenderType::SDR; } -} // namespace android \ No newline at end of file +/** + * Returns the maximum headroom allowed for this content under "idealized" + * display conditions (low surround luminance, high-enough display brightness). + * + * TODO: take into account hdr metadata, but square it with the fact that some + * HLG content has CTA.861-3 metadata + */ +inline float getIdealizedMaxHeadroom(ui::Dataspace dataspace) { + const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK; + + switch (transfer) { + case HAL_DATASPACE_TRANSFER_ST2084: + return 10000.0f / 203.0f; + case HAL_DATASPACE_TRANSFER_HLG: + return 1000.0f / 203.0f; + default: + return 1.0f; + } +} + +} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index f6d9a1ae6c..bb01946b2c 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "system/graphics-base-v1.0.h" #include @@ -398,11 +399,22 @@ void OutputLayer::updateCompositionState( // For hdr content, treat the white point as the display brightness - HDR content should not be // boosted or dimmed. // If the layer explicitly requests to disable dimming, then don't dim either. - if (hdrRenderType == HdrRenderType::GENERIC_HDR || - getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits || - getOutput().getState().displayBrightnessNits == 0.f || !layerFEState->dimmingEnabled) { + if (getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits || + getOutput().getState().displayBrightnessNits <= 0.f || !layerFEState->dimmingEnabled) { state.dimmingRatio = 1.f; state.whitePointNits = getOutput().getState().displayBrightnessNits; + } else if (hdrRenderType == HdrRenderType::GENERIC_HDR) { + float deviceHeadroom = getOutput().getState().displayBrightnessNits / + getOutput().getState().sdrWhitePointNits; + float idealizedMaxHeadroom = deviceHeadroom; + + if (FlagManager::getInstance().begone_bright_hlg()) { + idealizedMaxHeadroom = + std::min(idealizedMaxHeadroom, getIdealizedMaxHeadroom(state.dataspace)); + } + + state.dimmingRatio = std::min(idealizedMaxHeadroom / deviceHeadroom, 1.0f); + state.whitePointNits = getOutput().getState().displayBrightnessNits * state.dimmingRatio; } else { float layerBrightnessNits = getOutput().getState().sdrWhitePointNits; // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 37b35f2f63..6499001561 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3201,7 +3201,15 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, snapshot.desiredHdrSdrRatio < 1.f ? std::numeric_limits::infinity() : snapshot.desiredHdrSdrRatio; - info.mergeDesiredRatio(desiredHdrSdrRatio); + + float desiredRatio = desiredHdrSdrRatio; + if (FlagManager::getInstance().begone_bright_hlg() && + desiredHdrSdrRatio == + std::numeric_limits::infinity()) { + desiredRatio = getIdealizedMaxHeadroom(snapshot.dataspace); + } + + info.mergeDesiredRatio(desiredRatio); info.numberOfHdrLayers++; const auto displayFrame = outputLayer->getState().displayFrame; const int32_t area = diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index b56ee01422..a1b53ee738 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -160,6 +160,7 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(connected_display_hdr); DUMP_READ_ONLY_FLAG(deprecate_frame_tracker); DUMP_READ_ONLY_FLAG(skip_invisible_windows_in_input); + DUMP_READ_ONLY_FLAG(begone_bright_hlg); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -268,6 +269,7 @@ FLAG_MANAGER_READ_ONLY_FLAG(display_config_error_hal, ""); FLAG_MANAGER_READ_ONLY_FLAG(connected_display_hdr, ""); FLAG_MANAGER_READ_ONLY_FLAG(deprecate_frame_tracker, ""); FLAG_MANAGER_READ_ONLY_FLAG(skip_invisible_windows_in_input, ""); +FLAG_MANAGER_READ_ONLY_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg"); /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 9086537619..c1d88cecb2 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -98,6 +98,7 @@ public: bool connected_display_hdr() const; bool deprecate_frame_tracker() const; bool skip_invisible_windows_in_input() const; + bool begone_bright_hlg() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index d4250bcccc..1188435008 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -34,6 +34,14 @@ flag { is_fixed_read_only: true } # arr_surfacecontrol_setframerate_api +flag { + name: "begone_bright_hlg" + namespace: "core_graphics" + description: "Caps HLG brightness relative to SDR" + bug: "362510107" + is_fixed_read_only: true +} # begone_bright_hlg + flag { name: "ce_fence_promise" namespace: "window_surfaces" -- cgit v1.2.3-59-g8ed1b From ffbd83c6226273ae00781f42e52f485d895ee864 Mon Sep 17 00:00:00 2001 From: Harry Cutts Date: Mon, 18 Nov 2024 19:08:04 +0000 Subject: input: don't log the whole MotionEvent in index checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've had a crash report where this causes infinite recursion, probably through the following call sequence: operator<<(std::ostream&, const MotionEvent&) → MotionEvent::get(X|Y) → MotionEvent::getAxisValue → MotionEvent::getHistoricalAxisValue → MotionEvent::getHistoricalRawPointerCoords → operator<<(std::ostream&, const MotionEvent&) It's unclear how the MotionEvent gets corrupted such that getHistoricalRawPointerCoords is called with invalid indexes, but the simple fix is to only log a useful subset of the whole event in these checks. Bug: 379368465 Test: m checkinput Flag: EXEMPT bug fix Change-Id: I0822f88fc7da6ba08ba6dbbab71ca5aaf78fc35d --- libs/input/Input.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index a2bb3453fe..b87a7068b5 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -705,15 +705,17 @@ float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( size_t pointerIndex, size_t historicalIndex) const { if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) { - LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this; + LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex + << "; should be between >0 and ≤" << getPointerCount(); } if (CC_UNLIKELY(historicalIndex < 0 || historicalIndex > getHistorySize())) { - LOG(FATAL) << __func__ << ": Invalid historical index " << historicalIndex << " for " - << *this; + LOG(FATAL) << __func__ << ": Invalid historical index " << historicalIndex + << "; should be >0 and ≤" << getHistorySize(); } const size_t position = historicalIndex * getPointerCount() + pointerIndex; if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) { - LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this; + LOG(FATAL) << __func__ << ": Invalid array index " << position << "; should be >0 and ≤" + << mSamplePointerCoords.size(); } return &mSamplePointerCoords[position]; } -- cgit v1.2.3-59-g8ed1b From 9084218d1255891135976b3cf9c3f0f3afb9143a Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Thu, 14 Nov 2024 00:49:27 +0000 Subject: bufferqueues: Move the gl fence wait from dequeue to releaseBuffer Bug: 339705065 Flag: com.android.graphics.libgui.flags.bq_gl_fence_cleanup Test: old tests Change-Id: I429118e2f23691c8858100343f40b8cc156133ea --- libs/gui/BufferQueueConsumer.cpp | 27 +++++++++++++++++++++++++++ libs/gui/BufferQueueCore.cpp | 4 +++- libs/gui/BufferQueueProducer.cpp | 12 ++++++++++++ libs/gui/include/gui/BufferSlot.h | 28 +++++++++++++++++----------- 4 files changed, 59 insertions(+), 12 deletions(-) (limited to 'libs') diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index d0607bfab8..9855b5bca4 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -28,6 +28,10 @@ #define VALIDATE_CONSISTENCY() #endif +#define EGL_EGLEXT_PROTOTYPES +#include +#include + #include #include #include @@ -486,6 +490,27 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, return BAD_VALUE; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + if (eglFence != EGL_NO_SYNC_KHR) { + // Most platforms will be using native fences, so it's unlikely that we'll ever have to + // process an eglFence. Ideally we can remove this code eventually. In the mean time, do our + // best to wait for it so the buffer stays valid, otherwise return an error to the caller. + // + // EGL_SYNC_FLUSH_COMMANDS_BIT_KHR so that we don't wait forever on a fence that hasn't + // shown up on the GPU yet. + EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, + 1000000000); + if (result == EGL_FALSE) { + BQ_LOGE("releaseBuffer: error %#x waiting for fence", eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + BQ_LOGE("releaseBuffer: timeout waiting for fence"); + return UNKNOWN_ERROR; + } + eglDestroySyncKHR(eglDisplay, eglFence); + } +#endif + sp listener; { // Autolock scope std::lock_guard lock(mCore->mMutex); @@ -507,8 +532,10 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, return BAD_VALUE; } +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) mSlots[slot].mEglDisplay = eglDisplay; mSlots[slot].mEglFence = eglFence; +#endif mSlots[slot].mFence = releaseFence; mSlots[slot].mBufferState.release(); diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index d52cf700cd..5a093995b4 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -262,14 +262,16 @@ void BufferQueueCore::clearBufferSlotLocked(int slot) { mSlots[slot].mFrameNumber = 0; mSlots[slot].mAcquireCalled = false; mSlots[slot].mNeedsReallocation = true; + mSlots[slot].mFence = Fence::NO_FENCE; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) // Destroy fence as BufferQueue now takes ownership if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) { eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence); mSlots[slot].mEglFence = EGL_NO_SYNC_KHR; } - mSlots[slot].mFence = Fence::NO_FENCE; mSlots[slot].mEglDisplay = EGL_NO_DISPLAY; +#endif if (mLastQueuedSlot == slot) { mLastQueuedSlot = INVALID_BUFFER_SLOT; diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 39209f9745..2e7cef0847 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -451,8 +451,10 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou } status_t returnFlags = NO_ERROR; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) EGLDisplay eglDisplay = EGL_NO_DISPLAY; EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; +#endif bool attachedByConsumer = false; sp listener; @@ -569,8 +571,10 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = nullptr; mSlots[found].mRequestBufferCalled = false; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) mSlots[found].mEglDisplay = EGL_NO_DISPLAY; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; +#endif mSlots[found].mFence = Fence::NO_FENCE; mCore->mBufferAge = 0; mCore->mIsAllocating = true; @@ -595,14 +599,18 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou found, buffer->width, buffer->height, buffer->format); } +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) eglDisplay = mSlots[found].mEglDisplay; eglFence = mSlots[found].mEglFence; +#endif // Don't return a fence in shared buffer mode, except for the first // frame. *outFence = (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == found) ? Fence::NO_FENCE : mSlots[found].mFence; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) mSlots[found].mEglFence = EGL_NO_SYNC_KHR; +#endif mSlots[found].mFence = Fence::NO_FENCE; // If shared buffer mode has just been enabled, cache the slot of the @@ -691,6 +699,7 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou returnFlags |= BUFFER_NEEDS_REALLOCATION; } +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) if (eglFence != EGL_NO_SYNC_KHR) { EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0, 1000000000); @@ -705,6 +714,7 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou } eglDestroySyncKHR(eglDisplay, eglFence); } +#endif BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x", *outSlot, @@ -908,7 +918,9 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, mSlots[*outSlot].mGraphicBuffer = buffer; mSlots[*outSlot].mBufferState.attachProducer(); +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR; +#endif mSlots[*outSlot].mFence = Fence::NO_FENCE; mSlots[*outSlot].mRequestBufferCalled = true; mSlots[*outSlot].mAcquireCalled = false; diff --git a/libs/gui/include/gui/BufferSlot.h b/libs/gui/include/gui/BufferSlot.h index 5b32710135..e83d2e33f2 100644 --- a/libs/gui/include/gui/BufferSlot.h +++ b/libs/gui/include/gui/BufferSlot.h @@ -174,26 +174,30 @@ struct BufferState { }; struct BufferSlot { - BufferSlot() - : mGraphicBuffer(nullptr), - mEglDisplay(EGL_NO_DISPLAY), - mBufferState(), - mRequestBufferCalled(false), - mFrameNumber(0), - mEglFence(EGL_NO_SYNC_KHR), - mFence(Fence::NO_FENCE), - mAcquireCalled(false), - mNeedsReallocation(false) { + : mGraphicBuffer(nullptr), +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + mEglDisplay(EGL_NO_DISPLAY), +#endif + mBufferState(), + mRequestBufferCalled(false), + mFrameNumber(0), +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + mEglFence(EGL_NO_SYNC_KHR), +#endif + mFence(Fence::NO_FENCE), + mAcquireCalled(false), + mNeedsReallocation(false) { } // mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. sp mGraphicBuffer; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) // mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects. EGLDisplay mEglDisplay; - +#endif // mBufferState is the current state of this buffer slot. BufferState mBufferState; @@ -207,12 +211,14 @@ struct BufferSlot { // may be released before their release fence is signaled). uint64_t mFrameNumber; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) // mEglFence is the EGL sync object that must signal before the buffer // associated with this buffer slot may be dequeued. It is initialized // to EGL_NO_SYNC_KHR when the buffer is created and may be set to a // new sync object in releaseBuffer. (This is deprecated in favor of // mFence, below.) EGLSyncKHR mEglFence; +#endif // mFence is a fence which will signal when work initiated by the // previous owner of the buffer is finished. When the buffer is FREE, -- cgit v1.2.3-59-g8ed1b From 9cea07c5fc48f9a62d29905114db3ebdb6c74f4a Mon Sep 17 00:00:00 2001 From: Sally Qi Date: Mon, 18 Nov 2024 15:40:03 -0800 Subject: [Lut HAL backend] skip libtonemap if the lut(s) is in use Bug: 329472856 Test: builds Flag: EXEMPT bugfix Change-Id: Iaa0404629813074b5c65ba2ad47584491007fff5 --- libs/renderengine/skia/SkiaRenderEngine.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index a93f6c36ff..8f232492e9 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -572,8 +572,10 @@ sk_sp SkiaRenderEngine::createRuntimeEffectShader( } // disable tonemapping if we already locally tonemapped - auto inputDataspace = - usingLocalTonemap ? parameters.outputDataSpace : parameters.layer.sourceDataspace; + // skip tonemapping if the luts is in use + auto inputDataspace = usingLocalTonemap || (graphicBuffer && parameters.layer.luts) + ? parameters.outputDataSpace + : parameters.layer.sourceDataspace; auto effect = shaders::LinearEffect{.inputDataspace = inputDataspace, .outputDataspace = parameters.outputDataSpace, -- cgit v1.2.3-59-g8ed1b From f5fdff8d956bf61afd767c829810fa93b9909b4a Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Fri, 1 Nov 2024 09:28:47 -0600 Subject: Notify listeners about active picture profiles Bug: 337330263 Test: atest ActivePictureUpdaterTest Test: atest SurfaceControlPictureProfileTest Flag: com.android.graphics.libgui.flags.apply_picture_profiles Change-Id: If08b79faf3d3c4c07248ecd7385a75cfe5357726 --- libs/gui/SurfaceComposerClient.cpp | 7 + libs/gui/aidl/android/gui/ActivePicture.aidl | 32 ++ .../aidl/android/gui/IActivePictureListener.aidl | 30 ++ libs/gui/aidl/android/gui/ISurfaceComposer.aidl | 7 + libs/gui/include/gui/SurfaceComposerClient.h | 2 + libs/gui/tests/Surface_test.cpp | 5 + services/surfaceflinger/ActivePictureUpdater.cpp | 66 ++++ services/surfaceflinger/ActivePictureUpdater.h | 47 +++ services/surfaceflinger/Android.bp | 1 + .../include/compositionengine/LayerFE.h | 3 + .../include/compositionengine/mock/LayerFE.h | 1 + .../CompositionEngine/src/Output.cpp | 2 + .../CompositionEngine/src/OutputLayer.cpp | 2 +- .../CompositionEngine/tests/OutputTest.cpp | 10 + services/surfaceflinger/LayerFE.cpp | 11 +- services/surfaceflinger/LayerFE.h | 10 +- services/surfaceflinger/SurfaceFlinger.cpp | 81 ++++- services/surfaceflinger/SurfaceFlinger.h | 14 + .../tests/unittests/ActivePictureUpdaterTest.cpp | 336 +++++++++++++++++++++ .../tests/unittests/BackgroundExecutorTest.cpp | 16 + .../tests/unittests/mock/MockLayer.h | 9 +- 21 files changed, 670 insertions(+), 22 deletions(-) create mode 100644 libs/gui/aidl/android/gui/ActivePicture.aidl create mode 100644 libs/gui/aidl/android/gui/IActivePictureListener.aidl create mode 100644 services/surfaceflinger/ActivePictureUpdater.cpp create mode 100644 services/surfaceflinger/ActivePictureUpdater.h create mode 100644 services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp (limited to 'libs') diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 61aabaa7f6..1f7d16fccb 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -3280,6 +3280,13 @@ status_t SurfaceComposerClient::removeHdrLayerInfoListener( return statusTFromBinderStatus(status); } +status_t SurfaceComposerClient::setActivePictureListener( + const sp& listener) { + binder::Status status = + ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener); + return statusTFromBinderStatus(status); +} + status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) { binder::Status status = ComposerServiceAIDL::getComposerService()->notifyPowerBoost(boostId); return statusTFromBinderStatus(status); diff --git a/libs/gui/aidl/android/gui/ActivePicture.aidl b/libs/gui/aidl/android/gui/ActivePicture.aidl new file mode 100644 index 0000000000..9b83be16ca --- /dev/null +++ b/libs/gui/aidl/android/gui/ActivePicture.aidl @@ -0,0 +1,32 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** + * Visible content that is using picture processing. + * @hide + */ +parcelable ActivePicture { + /** The layer ID that is using picture processing. */ + int layerId; + + /** UID that owns layer using picture processing. */ + int ownerUid; + + /** ID of the picture profile that was used to configure the picture processing. */ + long pictureProfileId; +} diff --git a/libs/gui/aidl/android/gui/IActivePictureListener.aidl b/libs/gui/aidl/android/gui/IActivePictureListener.aidl new file mode 100644 index 0000000000..ad310bbd66 --- /dev/null +++ b/libs/gui/aidl/android/gui/IActivePictureListener.aidl @@ -0,0 +1,30 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.ActivePicture; + +/** + * Receive callbacks whenever the visible content using picture profiles changes. + * @hide + */ +interface IActivePictureListener { + /** + * Callback reporting the visible content on the screen using picture profiles. + */ + oneway void onActivePicturesChanged(in ActivePicture[] activePictures); +} diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index ac14138e8c..c8bf0ef444 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -33,6 +33,7 @@ import android.gui.FrameEvent; import android.gui.FrameStats; import android.gui.HdrConversionCapability; import android.gui.HdrConversionStrategy; +import android.gui.IActivePictureListener; import android.gui.IDisplayEventConnection; import android.gui.IFpsListener; import android.gui.IHdrLayerInfoListener; @@ -599,4 +600,10 @@ interface ISurfaceComposer { * past the provided VSync. */ oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync); + + /** + * Sets the listener used to monitor visible content that is being processed with picture + * profiles. + */ + oneway void setActivePictureListener(IActivePictureListener listener); } diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0d7f8c2824..ec62ec3dfb 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -298,6 +298,8 @@ public: static status_t removeHdrLayerInfoListener(const sp& displayToken, const sp& listener); + static status_t setActivePictureListener(const sp& listener); + /* * Sends a power boost to the composer. This function is asynchronous. * diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index c74186682e..38c7f2b70a 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -1015,6 +1016,10 @@ public: return binder::Status::ok(); } + binder::Status setActivePictureListener(const sp&) { + return binder::Status::ok(); + } + protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureUpdater.cpp new file mode 100644 index 0000000000..210e948a49 --- /dev/null +++ b/services/surfaceflinger/ActivePictureUpdater.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ActivePictureUpdater.h" + +#include + +#include "Layer.h" +#include "LayerFE.h" + +namespace android { + +void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& layerFE, + const CompositionResult& result) { + if (result.wasPictureProfileCommitted) { + gui::ActivePicture picture; + picture.layerId = int32_t(layer.sequence); + picture.ownerUid = int32_t(layer.getOwnerUid()); + // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state? + if (layerFE.getCompositionState()) { + picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId(); + } else { + picture.pictureProfileId = result.pictureProfileHandle.getId(); + } + mNewActivePictures.push_back(picture); + } +} + +bool ActivePictureUpdater::updateAndHasChanged() { + bool hasChanged = true; + if (mNewActivePictures.size() == mOldActivePictures.size()) { + auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int { + if (lhs.layerId == rhs.layerId) { + return lhs.pictureProfileId < rhs.pictureProfileId; + } + return lhs.layerId < rhs.layerId; + }; + std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare); + if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(), + mOldActivePictures.begin())) { + hasChanged = false; + } + } + std::swap(mOldActivePictures, mNewActivePictures); + mNewActivePictures.resize(0); + return hasChanged; +} + +const std::vector& ActivePictureUpdater::getActivePictures() const { + return mOldActivePictures; +} + +} // namespace android diff --git a/services/surfaceflinger/ActivePictureUpdater.h b/services/surfaceflinger/ActivePictureUpdater.h new file mode 100644 index 0000000000..20779bb0ff --- /dev/null +++ b/services/surfaceflinger/ActivePictureUpdater.h @@ -0,0 +1,47 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +namespace android { + +class Layer; +class LayerFE; +struct CompositionResult; + +// Keeps track of active pictures - layers that are undergoing picture processing. +class ActivePictureUpdater { +public: + // Called for each visible layer when SurfaceFlinger finishes composing. + void onLayerComposed(const Layer& layer, const LayerFE& layerFE, + const CompositionResult& result); + + // Update internals and return whether the set of active pictures have changed. + bool updateAndHasChanged(); + + // The current set of active pictures. + const std::vector& getActivePictures() const; + +private: + std::vector mOldActivePictures; + std::vector mNewActivePictures; +}; + +} // namespace android diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 8a667aef72..3f3d2c6cc6 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -199,6 +199,7 @@ filegroup { name: "libsurfaceflinger_sources", srcs: [ ":libsurfaceflinger_backend_sources", + "ActivePictureUpdater.cpp", "BackgroundExecutor.cpp", "Client.cpp", "ClientCache.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index a5e9dded8b..cda4edc216 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -161,6 +161,9 @@ public: // Checks if the buffer's release fence has been set virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0; + // Indicates that the picture profile request was applied to this layer. + virtual void onPictureProfileCommitted() = 0; + // Gets some kind of identifier for the layer for debug purposes. virtual const char* getDebugName() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index c7ff704491..272fa3eef7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -58,6 +58,7 @@ public: MOCK_CONST_METHOD0(hasRoundedCorners, bool()); MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*()); MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*()); + MOCK_METHOD0(onPictureProfileCommitted, void()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index ee813bf340..f9ed92d1ee 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -830,11 +830,13 @@ void Output::commitPictureProfilesToCompositionState() { for (int i = 0; i < getMaxLayerPictureProfiles() && !layersWithProfiles.empty(); layersWithProfiles.pop(), ++i) { layersWithProfiles.top()->commitPictureProfileToCompositionState(); + layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted(); } // No layer-specific picture processing, so apply the highest priority picture profile to // the entire display. } else if (!layersWithProfiles.empty()) { editState().pictureProfileHandle = layersWithProfiles.top()->getPictureProfileHandle(); + layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted(); } } diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index f6d9a1ae6c..65ded8b4c1 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -432,7 +432,7 @@ void OutputLayer::commitPictureProfileToCompositionState() { } const auto* layerState = getLayerFE().getCompositionState(); if (layerState) { - editState().pictureProfileHandle = getLayerFE().getCompositionState()->pictureProfileHandle; + editState().pictureProfileHandle = layerState->pictureProfileHandle; } } diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 99e68eb71a..442b603ca0 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -5117,6 +5117,11 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLay // Sets display picture profile to the highest priority layer's profile EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2))); + // Marks only the highest priority layer as committed + EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0); + EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted); + EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted).Times(0); + mOutput->editState().isEnabled = true; CompositionRefreshArgs args; args.updatingGeometryThisFrame = false; @@ -5172,6 +5177,11 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayer EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState); EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState); + // Marks only the highest priority layers as committed + EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0); + EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted); + EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted); + // No display picture profile is sent EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0); diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index 231b40b0ac..fea7671af2 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -342,8 +342,15 @@ void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster, caster.shadow = state; } -CompositionResult&& LayerFE::stealCompositionResult() { - return std::move(mCompositionResult); +void LayerFE::onPictureProfileCommitted() { + mCompositionResult.wasPictureProfileCommitted = true; + mCompositionResult.pictureProfileHandle = mSnapshot->pictureProfileHandle; +} + +CompositionResult LayerFE::stealCompositionResult() { + CompositionResult result; + std::swap(mCompositionResult, result); + return result; } const char* LayerFE::getDebugName() const { diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h index 5081e102b8..9483aebafa 100644 --- a/services/surfaceflinger/LayerFE.h +++ b/services/surfaceflinger/LayerFE.h @@ -18,6 +18,9 @@ #include #include +#include +#include + #include "FrontEnd/LayerSnapshot.h" #include "compositionengine/LayerFE.h" #include "compositionengine/LayerFECompositionState.h" @@ -29,6 +32,10 @@ namespace android { struct CompositionResult { sp lastClientCompositionFence = nullptr; + bool wasPictureProfileCommitted = false; + // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state? + // It would be better not to duplicate this information + PictureProfileHandle pictureProfileHandle = PictureProfileHandle::NONE; }; class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE { @@ -47,10 +54,11 @@ public: const gui::LayerMetadata* getRelativeMetadata() const override; std::optional prepareClientComposition( compositionengine::LayerFE::ClientCompositionTargetSettings&) const; - CompositionResult&& stealCompositionResult(); + CompositionResult stealCompositionResult(); ftl::Future createReleaseFenceFuture() override; void setReleaseFence(const FenceResult& releaseFence) override; LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override; + void onPictureProfileCommitted() override; std::unique_ptr mSnapshot; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1f35173f65..dfaf8b6c04 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -126,6 +126,7 @@ #include #include #include +#include "ActivePictureUpdater.h" #include "BackgroundExecutor.h" #include "Client.h" #include "ClientCache.h" @@ -372,6 +373,7 @@ const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER" const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER"); const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER"); const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS"); +const String16 sObservePictureProfiles("android.permission.OBSERVE_PICTURE_PROFILES"); const String16 sDump("android.permission.DUMP"); const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT"); const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW"); @@ -2851,6 +2853,9 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( if (compositionResult.lastClientCompositionFence) { layer->setWasClientComposed(compositionResult.lastClientCompositionFence); } + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + mActivePictureUpdater.onLayerComposed(*layer, *layerFE, compositionResult); + } } SFTRACE_NAME("postComposition"); @@ -3146,9 +3151,23 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, layer->releasePendingBuffer(presentTime.ns()); } + for (const auto& layerEvent : mLayerEvents) { + auto result = + stats::stats_write(stats::SURFACE_CONTROL_EVENT, + static_cast(layerEvent.uid), + static_cast(layerEvent.timeSinceLastEvent.count()), + static_cast(layerEvent.dataspace)); + if (result < 0) { + ALOGW("Failed to report layer event with error: %d", result); + } + } + mLayerEvents.clear(); + std::vector, sp>> hdrInfoListeners; - bool haveNewListeners = false; + bool haveNewHdrInfoListeners = false; + sp activePictureListener; + bool haveNewActivePictureListener = false; { Mutex::Autolock lock(mStateLock); if (mFpsReporter) { @@ -3158,6 +3177,7 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, if (mTunnelModeEnabledReporter) { mTunnelModeEnabledReporter->updateTunnelModeStatus(); } + hdrInfoListeners.reserve(mHdrLayerInfoListeners.size()); for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) { if (reporter && reporter->hasListeners()) { @@ -3166,24 +3186,15 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, } } } - haveNewListeners = mAddingHDRLayerInfoListener; // grab this with state lock + haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock mAddingHDRLayerInfoListener = false; - } - for (const auto& layerEvent : mLayerEvents) { - auto result = - stats::stats_write(stats::SURFACE_CONTROL_EVENT, - static_cast(layerEvent.uid), - static_cast(layerEvent.timeSinceLastEvent.count()), - static_cast(layerEvent.dataspace)); - if (result < 0) { - ALOGW("Failed to report layer event with error: %d", result); - } + activePictureListener = mActivePictureListener; + haveNewActivePictureListener = mHaveNewActivePictureListener; + mHaveNewActivePictureListener = false; } - mLayerEvents.clear(); - - if (haveNewListeners || mHdrLayerInfoChanged) { + if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) { for (auto& [compositionDisplay, listener] : hdrInfoListeners) { HdrLayerInfoReporter::HdrLayerInfo info; int32_t maxArea = 0; @@ -3233,9 +3244,19 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, listener->dispatchHdrLayerInfo(info); } } - mHdrLayerInfoChanged = false; + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + // Track, update and notify changes to active pictures - layers that are undergoing picture + // processing + if (mActivePictureUpdater.updateAndHasChanged() || haveNewActivePictureListener) { + if (activePictureListener) { + activePictureListener->onActivePicturesChanged( + mActivePictureUpdater.getActivePictures()); + } + } + } + mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */); mTransactionCallbackInvoker.clearCompletedTransactions(); @@ -8073,6 +8094,14 @@ void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t con })); } +void SurfaceFlinger::setActivePictureListener(const sp& listener) { + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + Mutex::Autolock lock(mStateLock); + mActivePictureListener = listener; + mHaveNewActivePictureListener = listener != nullptr; + } +} + std::shared_ptr SurfaceFlinger::getExternalTextureFromBufferData( BufferData& bufferData, const char* layerName, uint64_t transactionId) { if (bufferData.buffer && @@ -9008,6 +9037,15 @@ binder::Status SurfaceComposerAIDL::removeHdrLayerInfoListener( return binderStatusFromStatusT(status); } +binder::Status SurfaceComposerAIDL::setActivePictureListener( + const sp& listener) { + status_t status = checkObservePictureProfilesPermission(); + if (status == OK) { + mFlinger->setActivePictureListener(listener); + } + return binderStatusFromStatusT(status); +} + binder::Status SurfaceComposerAIDL::notifyPowerBoost(int boostId) { status_t status = checkAccessPermission(); if (status == OK) { @@ -9263,6 +9301,17 @@ status_t SurfaceComposerAIDL::checkReadFrameBufferPermission() { return OK; } +status_t SurfaceComposerAIDL::checkObservePictureProfilesPermission() { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(sObservePictureProfiles, pid, uid)) { + ALOGE("Permission Denial: can't manage picture profiles pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + return OK; +} + void SurfaceFlinger::forceFutureUpdate(int delayInMs) { static_cast(mScheduler->scheduleDelayed([&]() { scheduleRepaint(); }, ms2ns(delayInMs))); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 7e9d5b881f..cbe2739664 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -24,9 +24,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -67,6 +69,8 @@ #include #include +#include "ActivePictureUpdater.h" +#include "BackgroundExecutor.h" #include "Display/DisplayModeController.h" #include "Display/PhysicalDisplay.h" #include "DisplayDevice.h" @@ -93,6 +97,7 @@ #include "TransactionState.h" #include "Utils/OnceFuture.h" +#include #include #include #include @@ -660,6 +665,8 @@ private: void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel); + void setActivePictureListener(const sp& listener); + // IBinder::DeathRecipient overrides: void binderDied(const wp& who) override; @@ -1377,6 +1384,10 @@ private: std::unordered_map> mHdrLayerInfoListeners GUARDED_BY(mStateLock); + sp mActivePictureListener GUARDED_BY(mStateLock); + bool mHaveNewActivePictureListener GUARDED_BY(mStateLock); + ActivePictureUpdater mActivePictureUpdater GUARDED_BY(kMainThreadContext); + std::atomic mActiveDisplayTransformHint; // Must only be accessed on the main thread. @@ -1610,12 +1621,15 @@ public: binder::Status flushJankData(int32_t layerId) override; binder::Status removeJankListener(int32_t layerId, const sp& listener, int64_t afterVsync) override; + binder::Status setActivePictureListener(const sp& listener); + binder::Status clearActivePictureListener(); private: static const constexpr bool kUsePermissionCache = true; status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache); status_t checkControlDisplayBrightnessPermission(); status_t checkReadFrameBufferPermission(); + status_t checkObservePictureProfilesPermission(); static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info, gui::DynamicDisplayInfo*& outInfo); diff --git a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp new file mode 100644 index 0000000000..b926d2f4da --- /dev/null +++ b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp @@ -0,0 +1,336 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ActivePictureUpdater.h" +#include "LayerFE.h" +#include "TestableSurfaceFlinger.h" + +namespace android { + +using android::compositionengine::LayerFECompositionState; +using android::gui::ActivePicture; +using android::gui::IActivePictureListener; +using android::mock::MockLayer; +using surfaceflinger::frontend::LayerSnapshot; +using testing::_; +using testing::NiceMock; +using testing::Return; + +class TestableLayerFE : public LayerFE { +public: + TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) { + mSnapshot = std::unique_ptr(&snapshot); + } + + LayerSnapshot& snapshot; +}; + +class ActivePictureUpdaterTest : public testing::Test { +protected: + SurfaceFlinger* flinger() { + if (!mFlingerSetup) { + mFlinger.setupMockScheduler(); + mFlinger.setupComposer(std::make_unique()); + mFlinger.setupRenderEngine(std::make_unique()); + mFlingerSetup = true; + } + return mFlinger.flinger(); + } + +private: + TestableSurfaceFlinger mFlinger; + bool mFlingerSetup = false; +}; + +// Hack to workaround initializer lists not working for parcelables because parcelables inherit from +// Parcelable, which has a virtual destructor. +auto UnorderedElementsAre(std::initializer_list> tuples) { + std::vector activePictures; + for (auto tuple : tuples) { + ActivePicture ap; + ap.layerId = std::get<0>(tuple); + ap.ownerUid = std::get<1>(tuple); + ap.pictureProfileId = std::get<2>(tuple); + activePictures.push_back(ap); + } + return testing::UnorderedElementsAreArray(activePictures); +} + +// Parcelables don't define this for matchers, which is unfortunate +void PrintTo(const ActivePicture& activePicture, std::ostream* os) { + *os << activePicture.toString(); +} + +TEST_F(ActivePictureUpdaterTest, notCalledWithNoProfile) { + sp> layer = sp>::make(flinger(), 100); + TestableLayerFE layerFE; + EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + ActivePictureUpdater updater; + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE; + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_FALSE(updater.updateAndHasChanged()); + } +} + +TEST_F(ActivePictureUpdaterTest, calledWhenLayerStartsUsingProfile) { + sp> layer = sp>::make(flinger(), 100); + TestableLayerFE layerFE; + EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + ActivePictureUpdater updater; + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE; + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_FALSE(updater.updateAndHasChanged()); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); + } +} + +TEST_F(ActivePictureUpdaterTest, notCalledWhenLayerContinuesUsingProfile) { + sp> layer = sp>::make(flinger(), 100); + TestableLayerFE layerFE; + EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + ActivePictureUpdater updater; + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_FALSE(updater.updateAndHasChanged()); + } +} + +TEST_F(ActivePictureUpdaterTest, calledWhenLayerStopsUsingProfile) { + sp> layer = sp>::make(flinger(), 100); + TestableLayerFE layerFE; + EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + ActivePictureUpdater updater; + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE; + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({})); + } +} + +TEST_F(ActivePictureUpdaterTest, calledWhenLayerChangesProfile) { + sp> layer = sp>::make(flinger(), 100); + TestableLayerFE layerFE; + EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + ActivePictureUpdater updater; + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE.onPictureProfileCommitted(); + updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 2}})); + } +} + +TEST_F(ActivePictureUpdaterTest, notCalledWhenUncommittedLayerChangesProfile) { + sp> layer1 = sp>::make(flinger(), 100); + TestableLayerFE layerFE1; + EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + sp> layer2 = sp>::make(flinger(), 200); + TestableLayerFE layerFE2; + EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20))); + + ActivePictureUpdater updater; + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); + updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); + } + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); + updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + ASSERT_FALSE(updater.updateAndHasChanged()); + } +} + +TEST_F(ActivePictureUpdaterTest, calledWhenDifferentLayerUsesSameProfile) { + sp> layer1 = sp>::make(flinger(), 100); + TestableLayerFE layerFE1; + EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + sp> layer2 = sp>::make(flinger(), 200); + TestableLayerFE layerFE2; + EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20))); + + ActivePictureUpdater updater; + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE2.onPictureProfileCommitted(); + updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), + UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}})); + } + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE1.onPictureProfileCommitted(); + updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE2.onPictureProfileCommitted(); + updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), + UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}})); + } +} + +TEST_F(ActivePictureUpdaterTest, calledWhenSameUidUsesSameProfile) { + sp> layer1 = sp>::make(flinger(), 100); + TestableLayerFE layerFE1; + EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + sp> layer2 = sp>::make(flinger(), 200); + TestableLayerFE layerFE2; + EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + ActivePictureUpdater updater; + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE2.onPictureProfileCommitted(); + updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), + UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}})); + } + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE1.onPictureProfileCommitted(); + updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE2.onPictureProfileCommitted(); + updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), + UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}})); + } +} + +TEST_F(ActivePictureUpdaterTest, calledWhenNewLayerUsesSameProfile) { + sp> layer1 = sp>::make(flinger(), 100); + TestableLayerFE layerFE1; + EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + ActivePictureUpdater updater; + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); + } + + sp> layer2 = sp>::make(flinger(), 200); + TestableLayerFE layerFE2; + EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); + + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE2.onPictureProfileCommitted(); + updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + ASSERT_TRUE(updater.updateAndHasChanged()); + EXPECT_THAT(updater.getActivePictures(), + UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}})); + } +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp index 5413bae55c..72d1351eb4 100644 --- a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp +++ b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp @@ -1,3 +1,19 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include #include diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index 45f86fa1a8..59f0966047 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -19,6 +19,8 @@ #include #include +#include "Layer.h" + namespace android::mock { class MockLayer : public Layer { @@ -26,8 +28,11 @@ public: MockLayer(SurfaceFlinger* flinger, std::string name) : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {} - MockLayer(SurfaceFlinger* flinger, std::string name, std::optional uid) - : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {} + MockLayer(SurfaceFlinger* flinger, std::string name, std::optional id) + : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, id)) {} + + MockLayer(SurfaceFlinger* flinger, std::optional id) + : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 0, {}, id)) {} explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {} -- cgit v1.2.3-59-g8ed1b From fc4f2817e10f4c145ca51a06de208565aa23c3ad Mon Sep 17 00:00:00 2001 From: Shan Huang Date: Fri, 1 Nov 2024 18:33:43 -0700 Subject: Tweak Kawase2 to use fewer passes and fewer samples. Test: atest librenderengine_bench Bug: 353826438 Flag: EXEMPT behind sysprop flag already Change-Id: Ifcc4e76aa35eb0793734e085ccaeba7d468e4bce --- .../skia/filters/KawaseBlurDualFilter.cpp | 53 ++++++++++++---------- 1 file changed, 30 insertions(+), 23 deletions(-) (limited to 'libs') diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp index db0b133a26..da47aae15b 100644 --- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp @@ -96,13 +96,17 @@ void KawaseBlurDualFilter::blurInto(const sk_sp& drawSurface, void KawaseBlurDualFilter::blurInto(const sk_sp& drawSurface, sk_sp input, const float inverseScale, const float radius, const float alpha) const { - SkRuntimeShaderBuilder blurBuilder(mBlurEffect); - blurBuilder.child("child") = std::move(input); - blurBuilder.uniform("in_inverseScale") = inverseScale; - blurBuilder.uniform("in_blurOffset") = radius; - blurBuilder.uniform("in_crossFade") = alpha; SkPaint paint; - paint.setShader(blurBuilder.makeShader(nullptr)); + if (radius == 0) { + paint.setShader(std::move(input)); + paint.setAlphaf(alpha); + } else { + SkRuntimeShaderBuilder blurBuilder(mBlurEffect); + blurBuilder.child("child") = std::move(input); + blurBuilder.uniform("in_blurOffset") = radius; + blurBuilder.uniform("in_crossFade") = alpha; + paint.setShader(blurBuilder.makeShader(nullptr)); + } paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver); drawSurface->getCanvas()->drawPaint(paint); } @@ -116,32 +120,35 @@ sk_sp KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin // Use a variable number of blur passes depending on the radius. The non-integer part of this // calculation is used to mix the final pass into the second-last with an alpha blend. - constexpr int kMaxSurfaces = 4; - const float filterDepth = - std::min(kMaxSurfaces - 1.0f, 1.0f + std::max(0.0f, log2f(radius * kInputScale))); + constexpr int kMaxSurfaces = 3; + const float filterDepth = std::min(kMaxSurfaces - 1.0f, radius * kInputScale / 2.5f); const int filterPasses = std::min(kMaxSurfaces - 1, static_cast(ceil(filterDepth))); - // Render into surfaces downscaled by 1x, 1x, 2x, and 4x from the initial downscale. + // Render into surfaces downscaled by 1x, 2x, and 4x from the initial downscale. sk_sp surfaces[kMaxSurfaces] = {filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr, - filterPasses >= 1 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr, - filterPasses >= 2 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr, - filterPasses >= 3 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr}; - - // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 600. - static const float kWeights[7] = {1.0f, 2.0f, 3.5f, 1.0f, 2.0f, 2.0f, 2.0f}; + filterPasses >= 1 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr, + filterPasses >= 2 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr}; + + // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 250. + static const float kWeights[5] = { + 1.0f, // 1st downsampling pass + 1.0f, // 2nd downsampling pass + 1.0f, // 3rd downsampling pass + 0.0f, // 1st upscaling pass. Set to zero to upscale without blurring for performance. + 1.0f, // 2nd upscaling pass + }; // Kawase is an approximation of Gaussian, but behaves differently because it is made up of many // simpler blurs. A transformation is required to approximate the same effect as Gaussian. - float sumSquaredR = powf(kWeights[0] * powf(2.0f, 1), 2.0f); + float sumSquaredR = powf(kWeights[0], 2.0f); for (int i = 0; i < filterPasses; i++) { const float alpha = std::min(1.0f, filterDepth - i); - sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[1 + i], 2.0f); - sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[6 - i], 2.0f); + sumSquaredR += powf(powf(2.0f, i) * alpha * kWeights[1 + i] / kInputScale, 2.0f); + sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[4 - i] / kInputScale, 2.0f); } - // Solve for R = sqrt(sum(r_i^2)). Divide R by hypot(1,1) to find some (x,y) offsets. - const float step = M_SQRT1_2 * - sqrtf(max(0.0f, (powf(radius, 2.0f) - powf(kInverseInputScale, 2.0f)) / sumSquaredR)); + // Solve for R = sqrt(sum(r_i^2)). + const float step = radius * sqrt(1.0f / sumSquaredR); // Start by downscaling and doing the first blur pass. { @@ -162,7 +169,7 @@ sk_sp KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin } // Finally blur+upscale back to our original size. for (int i = filterPasses - 1; i >= 0; i--) { - blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[6 - i] * step, + blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[4 - i] * step, std::min(1.0f, filterDepth - i)); } return surfaces[0]->makeImageSnapshot(); -- cgit v1.2.3-59-g8ed1b From 5541f12784132f69629ad084cc4a8477bb40644e Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Tue, 12 Nov 2024 13:22:34 -0700 Subject: Add API to retrieve max layer picture profiles Bug: 337330263 Test: atest SurfaceControlPictureProfileTest Flag: com.android.graphics.libgui.flags.apply_picture_profiles Change-Id: Idcfc3acd2e9eb89dd72baf0270d5d2a003d1f996 --- libs/gui/SurfaceComposerClient.cpp | 8 +++++++ libs/gui/aidl/android/gui/ISurfaceComposer.aidl | 5 +++++ libs/gui/include/gui/SurfaceComposerClient.h | 9 ++++++++ libs/gui/tests/Surface_test.cpp | 5 +++++ services/surfaceflinger/SurfaceFlinger.cpp | 28 +++++++++++++++++++++++++ services/surfaceflinger/SurfaceFlinger.h | 3 +++ 6 files changed, 58 insertions(+) (limited to 'libs') diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 1f7d16fccb..793377668c 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -3056,6 +3056,14 @@ void SurfaceComposerClient::setDisplayPowerMode(const sp& token, ComposerServiceAIDL::getComposerService()->setPowerMode(token, mode); } +status_t SurfaceComposerClient::getMaxLayerPictureProfiles(const sp& token, + int32_t* outMaxProfiles) { + binder::Status status = + ComposerServiceAIDL::getComposerService()->getMaxLayerPictureProfiles(token, + outMaxProfiles); + return statusTFromBinderStatus(status); +} + status_t SurfaceComposerClient::getCompositionPreference( ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat, ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) { diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index c8bf0ef444..8c19bbbba9 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -228,6 +228,11 @@ interface ISurfaceComposer { */ void setGameContentType(IBinder display, boolean on); + /** + * Gets the maximum number of picture profiles supported by the display. + */ + int getMaxLayerPictureProfiles(IBinder display); + /** * Capture the specified screen. This requires READ_FRAME_BUFFER * permission. This function will fail if there is a secure window on diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index ec62ec3dfb..0ce0c0a9c3 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -345,6 +345,15 @@ public: static bool flagEdgeExtensionEffectUseShader(); + /** + * Returns how many picture profiles are supported by the display. + * + * displayToken + * The token of the display. + */ + static status_t getMaxLayerPictureProfiles(const sp& displayToken, + int32_t* outMaxProfiles); + // ------------------------------------------------------------------------ // surface creation / destruction diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 38c7f2b70a..76362ff272 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -1020,6 +1020,11 @@ public: return binder::Status::ok(); } + binder::Status getMaxLayerPictureProfiles(const sp& /*display*/, + int32_t* /*outMaxProfiles*/) { + return binder::Status::ok(); + } + protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index dfaf8b6c04..4c705bc3d1 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1840,6 +1840,24 @@ void SurfaceFlinger::setGameContentType(const sp& displayToken, bool on })); } +status_t SurfaceFlinger::getMaxLayerPictureProfiles(const sp& displayToken, + int32_t* outMaxProfiles) { + const char* const whence = __func__; + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { + const ssize_t index = mCurrentState.displays.indexOfKey(displayToken); + if (index < 0) { + ALOGE("%s: Invalid display token %p", whence, displayToken.get()); + return 0; + } + const DisplayDeviceState& state = mCurrentState.displays.valueAt(index); + return state.maxLayerPictureProfiles > 0 ? state.maxLayerPictureProfiles + : state.hasPictureProcessing ? 1 + : 0; + }); + *outMaxProfiles = future.get(); + return NO_ERROR; +} + status_t SurfaceFlinger::overrideHdrTypes(const sp& displayToken, const std::vector& hdrTypes) { Mutex::Autolock lock(mStateLock); @@ -8759,6 +8777,16 @@ binder::Status SurfaceComposerAIDL::setGameContentType(const sp& displa return binder::Status::ok(); } +binder::Status SurfaceComposerAIDL::getMaxLayerPictureProfiles(const sp& display, + int32_t* outMaxProfiles) { + status_t status = checkAccessPermission(); + if (status != OK) { + return binderStatusFromStatusT(status); + } + mFlinger->getMaxLayerPictureProfiles(display, outMaxProfiles); + return binder::Status::ok(); +} + binder::Status SurfaceComposerAIDL::captureDisplay( const DisplayCaptureArgs& args, const sp& captureListener) { mFlinger->captureDisplay(args, captureListener); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index cbe2739664..211f37478a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -596,6 +596,7 @@ private: status_t getHdrOutputConversionSupport(bool* outSupport) const; void setAutoLowLatencyMode(const sp& displayToken, bool on); void setGameContentType(const sp& displayToken, bool on); + status_t getMaxLayerPictureProfiles(const sp& displayToken, int32_t* outMaxProfiles); void setPowerMode(const sp& displayToken, int mode); status_t overrideHdrTypes(const sp& displayToken, const std::vector& hdrTypes); @@ -1533,6 +1534,8 @@ public: binder::Status getHdrOutputConversionSupport(bool* outSupport) override; binder::Status setAutoLowLatencyMode(const sp& display, bool on) override; binder::Status setGameContentType(const sp& display, bool on) override; + binder::Status getMaxLayerPictureProfiles(const sp& display, + int32_t* outMaxProfiles) override; binder::Status captureDisplay(const DisplayCaptureArgs&, const sp&) override; binder::Status captureDisplayById(int64_t, const CaptureArgs&, -- cgit v1.2.3-59-g8ed1b From 2358c1360dee84841d9d405cb0de5fa199fd0cf1 Mon Sep 17 00:00:00 2001 From: Harry Cutts Date: Tue, 19 Nov 2024 18:34:59 +0000 Subject: MotionEvent: add safe dumping method This method should allow us to log more details of an event in case of validation failures, without potential for infinite recursion in getter methods. Bug: 379368465 Test: add a test to MotionEventTest that calls getHistoricalRawPointerCoords with an invalid pointer index, and check the logs when run with `atest --host` Flag: EXEMPT logs only Change-Id: I9c7084cedbc7e6f6834cd1b401da04d07d22ce35 --- include/input/Input.h | 9 ++++++++ libs/input/Input.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 8 deletions(-) (limited to 'libs') diff --git a/include/input/Input.h b/include/input/Input.h index a8684bd19b..0e330e453b 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -994,6 +994,15 @@ protected: std::vector mPointerProperties; std::vector mSampleEventTimes; std::vector mSamplePointerCoords; + +private: + /** + * Create a human-readable string representation of the event's data for debugging purposes. + * + * Unlike operator<<, this method does not assume that the event data is valid or consistent, or + * call any accessor methods that might themselves call safeDump in the case of invalid data. + */ + std::string safeDump() const; }; std::ostream& operator<<(std::ostream& out, const MotionEvent& event); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index b87a7068b5..65a088eb6d 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -685,11 +685,12 @@ void MotionEvent::setCursorPosition(float x, float y) { const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) { - LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this; + LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " + << safeDump(); } const size_t position = getHistorySize() * getPointerCount() + pointerIndex; if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) { - LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this; + LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << safeDump(); } return &mSamplePointerCoords[position]; } @@ -705,17 +706,16 @@ float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( size_t pointerIndex, size_t historicalIndex) const { if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) { - LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex - << "; should be between >0 and ≤" << getPointerCount(); + LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " + << safeDump(); } if (CC_UNLIKELY(historicalIndex < 0 || historicalIndex > getHistorySize())) { - LOG(FATAL) << __func__ << ": Invalid historical index " << historicalIndex - << "; should be >0 and ≤" << getHistorySize(); + LOG(FATAL) << __func__ << ": Invalid historical index " << historicalIndex << " for " + << safeDump(); } const size_t position = historicalIndex * getPointerCount() + pointerIndex; if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) { - LOG(FATAL) << __func__ << ": Invalid array index " << position << "; should be >0 and ≤" - << mSamplePointerCoords.size(); + LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << safeDump(); } return &mSamplePointerCoords[position]; } @@ -1146,6 +1146,53 @@ bool MotionEvent::operator==(const android::MotionEvent& o) const { // clang-format on } +std::string MotionEvent::safeDump() const { + std::stringstream out; + // Field names have the m prefix here to make it easy to distinguish safeDump output from + // operator<< output in logs. + out << "MotionEvent { mAction=" << MotionEvent::actionToString(mAction); + if (mActionButton != 0) { + out << ", mActionButton=" << mActionButton; + } + if (mButtonState != 0) { + out << ", mButtonState=" << mButtonState; + } + if (mClassification != MotionClassification::NONE) { + out << ", mClassification=" << motionClassificationToString(mClassification); + } + if (mMetaState != 0) { + out << ", mMetaState=" << mMetaState; + } + if (mFlags != 0) { + out << ", mFlags=0x" << std::hex << mFlags << std::dec; + } + if (mEdgeFlags != 0) { + out << ", mEdgeFlags=" << mEdgeFlags; + } + out << ", mDownTime=" << mDownTime; + out << ", mDeviceId=" << mDeviceId; + out << ", mSource=" << inputEventSourceToString(mSource); + out << ", mDisplayId=" << mDisplayId; + out << ", mEventId=0x" << std::hex << mId << std::dec; + // Since we're not assuming the data is at all valid, we also limit the number of items that + // might be printed from vectors, in case the vector's size field is corrupted. + out << ", mPointerProperties=(" << mPointerProperties.size() << ")["; + for (size_t i = 0; i < mPointerProperties.size() && i < MAX_POINTERS; i++) { + out << (i > 0 ? ", " : "") << mPointerProperties.at(i); + } + out << "], mSampleEventTimes=(" << mSampleEventTimes.size() << ")["; + for (size_t i = 0; i < mSampleEventTimes.size() && i < 256; i++) { + out << (i > 0 ? ", " : "") << mSampleEventTimes.at(i); + } + out << "], mSamplePointerCoords=(" << mSamplePointerCoords.size() << ")["; + for (size_t i = 0; i < mSamplePointerCoords.size() && i < MAX_POINTERS; i++) { + const PointerCoords& coords = mSamplePointerCoords.at(i); + out << (i > 0 ? ", " : "") << "(" << coords.getX() << ", " << coords.getY() << ")"; + } + out << "] }"; + return out.str(); +} + std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction()); if (event.getActionButton() != 0) { -- cgit v1.2.3-59-g8ed1b From bee19021e3f9136d3762d764144c738da13f4bde Mon Sep 17 00:00:00 2001 From: ramindani Date: Tue, 29 Oct 2024 09:20:22 -0700 Subject: Adds getSupportedRefreshRates support Source values for refresh rates from the RefreshRateSelector through SurfaceComposerClient. Test: atest android.display.cts.DisplayTest BUG: 365163968 Flag: com.android.server.display.feature.flags.enable_get_supported_refresh_rates Change-Id: I149e6e51b3b3718ef53e522f1fca5650dbbd8b7b --- libs/gui/SurfaceComposerClient.cpp | 5 +++++ libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl | 3 +++ libs/ui/include/ui/DynamicDisplayInfo.h | 3 +++ services/surfaceflinger/Scheduler/RefreshRateSelector.cpp | 13 +++++++++++++ services/surfaceflinger/Scheduler/RefreshRateSelector.h | 2 ++ services/surfaceflinger/SurfaceFlinger.cpp | 7 +++++++ 6 files changed, 33 insertions(+) (limited to 'libs') diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 61aabaa7f6..2603977466 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -2873,6 +2873,11 @@ void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInf outInfo->hasArrSupport = ginfo.hasArrSupport; outInfo->frameRateCategoryRate = ui::FrameRateCategoryRate(ginfo.frameRateCategoryRate.normal, ginfo.frameRateCategoryRate.high); + outInfo->supportedRefreshRates.clear(); + outInfo->supportedRefreshRates.reserve(ginfo.supportedRefreshRates.size()); + for (const auto rate : ginfo.supportedRefreshRates) { + outInfo->supportedRefreshRates.push_back(static_cast(rate)); + } } status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId, diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl index 67cc273fce..26c12c56f3 100644 --- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl +++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl @@ -50,4 +50,7 @@ parcelable DynamicDisplayInfo { // Represents frame rate for FrameRateCategory Normal and High. FrameRateCategoryRate frameRateCategoryRate; + + // All the refresh rates supported for the default display mode. + float[] supportedRefreshRates; } diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h index af494dcf39..9d97151155 100644 --- a/libs/ui/include/ui/DynamicDisplayInfo.h +++ b/libs/ui/include/ui/DynamicDisplayInfo.h @@ -59,6 +59,9 @@ struct DynamicDisplayInfo { // Represents frame rate for FrameRateCategory Normal and High. ui::FrameRateCategoryRate frameRateCategoryRate; + + // All the refresh rates supported for the default display mode. + std::vector supportedRefreshRates; }; } // namespace android::ui diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index 84fa1390a4..7f763e565d 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -1569,6 +1569,19 @@ Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const { return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound); } +std::vector RefreshRateSelector::getSupportedFrameRates() const { + std::scoped_lock lock(mLock); + // TODO(b/356986687) Remove the limit once we have the anchor list implementation. + const size_t frameRatesSize = std::min(11, mPrimaryFrameRates.size()); + std::vector supportedFrameRates; + supportedFrameRates.reserve(frameRatesSize); + std::transform(mPrimaryFrameRates.rbegin(), + mPrimaryFrameRates.rbegin() + static_cast(frameRatesSize), + std::back_inserter(supportedFrameRates), + [](FrameRateMode mode) { return mode.fps.getValue(); }); + return supportedFrameRates; +} + auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction { std::lock_guard lock(mLock); diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index ee3a4f7bdc..508f9d72ea 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -441,6 +441,8 @@ public: std::pair getFrameRateCategoryRates() const { return kFrameRateCategoryRates; } + std::vector getSupportedFrameRates() const EXCLUDES(mLock); + private: friend struct TestableRefreshRateSelector; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 9854174bbd..de94c264a8 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1229,6 +1229,8 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info const auto [normal, high] = display->refreshRateSelector().getFrameRateCategoryRates(); ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue()); info->frameRateCategoryRate = frameRateCategoryRate; + + info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates(); info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities()); @@ -8581,6 +8583,11 @@ void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& gui::FrameRateCategoryRate& frameRateCategoryRate = outInfo->frameRateCategoryRate; frameRateCategoryRate.normal = info.frameRateCategoryRate.getNormal(); frameRateCategoryRate.high = info.frameRateCategoryRate.getHigh(); + outInfo->supportedRefreshRates.clear(); + outInfo->supportedRefreshRates.reserve(info.supportedRefreshRates.size()); + for (float supportedRefreshRate : info.supportedRefreshRates) { + outInfo->supportedRefreshRates.push_back(supportedRefreshRate); + } outInfo->supportedColorModes.clear(); outInfo->supportedColorModes.reserve(info.supportedColorModes.size()); -- cgit v1.2.3-59-g8ed1b From 74befc9eaef3521ea575776e1ed2c75d49fb8b8e Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 20 Nov 2024 22:58:37 +0000 Subject: libbinder: deprecated notice for *CString These APIs are super custom and not used by AIDL. Don't manually write parceling code, and don't use CString with binder for sure either. Bug: 376674798 Test: N/A Change-Id: I8520c24f3a23f0e65e17c08e4e719dd410037c06 --- libs/binder/include/binder/Parcel.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index ceab20a275..0c7366e683 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -178,7 +178,8 @@ public: LIBBINDER_EXPORTED status_t writeUint64(uint64_t val); LIBBINDER_EXPORTED status_t writeFloat(float val); LIBBINDER_EXPORTED status_t writeDouble(double val); - LIBBINDER_EXPORTED status_t writeCString(const char* str); + LIBBINDER_EXPORTED status_t writeCString(const char* str) + __attribute__((deprecated("use AIDL, writeString* instead"))); LIBBINDER_EXPORTED status_t writeString8(const String8& str); LIBBINDER_EXPORTED status_t writeString8(const char* str, size_t len); LIBBINDER_EXPORTED status_t writeString16(const String16& str); @@ -434,7 +435,8 @@ public: LIBBINDER_EXPORTED status_t readUtf8FromUtf16(std::unique_ptr* str) const __attribute__((deprecated("use std::optional version instead"))); - LIBBINDER_EXPORTED const char* readCString() const; + LIBBINDER_EXPORTED const char* readCString() const + __attribute__((deprecated("use AIDL, use readString*"))); LIBBINDER_EXPORTED String8 readString8() const; LIBBINDER_EXPORTED status_t readString8(String8* pArg) const; LIBBINDER_EXPORTED const char* readString8Inplace(size_t* outLen) const; -- cgit v1.2.3-59-g8ed1b From 53463397c3b84bb71d7fe155a8ada86fdcc0e96b Mon Sep 17 00:00:00 2001 From: Paul Ramirez Date: Mon, 11 Nov 2024 21:49:51 +0000 Subject: Fix One Euro filter's units of computation Changed the units that the One Euro filter uses to compute the filtered coordinates. This was causing a crash because if two timestamps were sufficiently close to each other, by the time of implicitly converting from nanoseconds to seconds, they were considered equal. This led to a zero division when calculating the sampling frequency. Now, everything is handled in the scale of nanoseconds, and conversion are done if and only if they're necessary. Bug: 297226446 Flag: EXEMPT bugfix Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST Change-Id: I7fced6db447074cccb3d938eb9dc7a9707433f53 --- include/input/CoordinateFilter.h | 2 +- include/input/OneEuroFilter.h | 10 +- libs/input/CoordinateFilter.cpp | 2 +- libs/input/OneEuroFilter.cpp | 34 ++-- libs/input/tests/Android.bp | 1 + .../tests/InputConsumerFilteredResampling_test.cpp | 218 +++++++++++++++++++++ libs/input/tests/OneEuroFilter_test.cpp | 5 +- libs/input/tests/TestEventMatchers.h | 9 +- 8 files changed, 258 insertions(+), 23 deletions(-) create mode 100644 libs/input/tests/InputConsumerFilteredResampling_test.cpp (limited to 'libs') diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h index f36472dc8c..8f2e605e85 100644 --- a/include/input/CoordinateFilter.h +++ b/include/input/CoordinateFilter.h @@ -44,7 +44,7 @@ public: * the previous call. * @param coords Coordinates to be overwritten by the corresponding filtered coordinates. */ - void filter(std::chrono::duration timestamp, PointerCoords& coords); + void filter(std::chrono::nanoseconds timestamp, PointerCoords& coords); private: OneEuroFilter mXFilter; diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h index a0168e4f91..bdd82b2ee8 100644 --- a/include/input/OneEuroFilter.h +++ b/include/input/OneEuroFilter.h @@ -56,7 +56,7 @@ public: * provided in the previous call. * @param rawPosition Position to be filtered. */ - float filter(std::chrono::duration timestamp, float rawPosition); + float filter(std::chrono::nanoseconds timestamp, float rawPosition); private: /** @@ -67,7 +67,7 @@ private: /** * Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the - * filtered signal's speed. The data member is dimensionless, that is, it does not have units. + * filtered signal's speed. Units are 1 / position. */ const float mBeta; @@ -78,9 +78,9 @@ private: const float mSpeedCutoffFreq; /** - * The timestamp from the previous call. Units are seconds. + * The timestamp from the previous call. */ - std::optional> mPrevTimestamp; + std::optional mPrevTimestamp; /** * The raw position from the previous call. @@ -88,7 +88,7 @@ private: std::optional mPrevRawPosition; /** - * The filtered velocity from the previous call. Units are position per second. + * The filtered velocity from the previous call. Units are position per nanosecond. */ std::optional mPrevFilteredVelocity; diff --git a/libs/input/CoordinateFilter.cpp b/libs/input/CoordinateFilter.cpp index d231474577..a32685bd53 100644 --- a/libs/input/CoordinateFilter.cpp +++ b/libs/input/CoordinateFilter.cpp @@ -23,7 +23,7 @@ namespace android { CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta) : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {} -void CoordinateFilter::filter(std::chrono::duration timestamp, PointerCoords& coords) { +void CoordinateFilter::filter(std::chrono::nanoseconds timestamp, PointerCoords& coords) { coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX())); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY())); } diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp index 400d7c9ab0..7b0d104da1 100644 --- a/libs/input/OneEuroFilter.cpp +++ b/libs/input/OneEuroFilter.cpp @@ -25,16 +25,24 @@ namespace android { namespace { +using namespace std::literals::chrono_literals; + +const float kHertzPerGigahertz = 1E9f; +const float kGigahertzPerHertz = 1E-9f; + +// filteredSpeed's units are position per nanosecond. beta's units are 1 / position. inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) { - return minCutoffFreq + beta * std::abs(filteredSpeed); + return kHertzPerGigahertz * + ((minCutoffFreq * kGigahertzPerHertz) + beta * std::abs(filteredSpeed)); } -inline float smoothingFactor(std::chrono::duration samplingPeriod, float cutoffFreq) { - return samplingPeriod.count() / (samplingPeriod.count() + (1.0 / (2.0 * M_PI * cutoffFreq))); +inline float smoothingFactor(std::chrono::nanoseconds samplingPeriod, float cutoffFreq) { + const float constant = 2.0f * M_PI * samplingPeriod.count() * (cutoffFreq * kGigahertzPerHertz); + return constant / (constant + 1); } -inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) { - return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition; +inline float lowPassFilter(float rawValue, float prevFilteredValue, float smoothingFactor) { + return smoothingFactor * rawValue + (1 - smoothingFactor) * prevFilteredValue; } } // namespace @@ -42,17 +50,17 @@ inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq) : mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {} -float OneEuroFilter::filter(std::chrono::duration timestamp, float rawPosition) { - LOG_IF(FATAL, mPrevFilteredPosition.has_value() && (timestamp <= *mPrevTimestamp)) - << "Timestamp must be greater than mPrevTimestamp"; +float OneEuroFilter::filter(std::chrono::nanoseconds timestamp, float rawPosition) { + LOG_IF(FATAL, mPrevTimestamp.has_value() && (*mPrevTimestamp >= timestamp)) + << "Timestamp must be greater than mPrevTimestamp. Timestamp: " << timestamp.count() + << "ns. mPrevTimestamp: " << mPrevTimestamp->count() << "ns"; - const std::chrono::duration samplingPeriod = (mPrevTimestamp.has_value()) - ? (timestamp - *mPrevTimestamp) - : std::chrono::duration{1.0}; + const std::chrono::nanoseconds samplingPeriod = + (mPrevTimestamp.has_value()) ? (timestamp - *mPrevTimestamp) : 1s; const float rawVelocity = (mPrevFilteredPosition.has_value()) - ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count()) - : 0.0; + ? ((rawPosition - *mPrevFilteredPosition) / (samplingPeriod.count())) + : 0.0f; const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq); diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 46e819061f..d1c564d020 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -17,6 +17,7 @@ cc_test { "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputConsumer_test.cpp", + "InputConsumerFilteredResampling_test.cpp", "InputConsumerResampling_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", diff --git a/libs/input/tests/InputConsumerFilteredResampling_test.cpp b/libs/input/tests/InputConsumerFilteredResampling_test.cpp new file mode 100644 index 0000000000..757cd18a38 --- /dev/null +++ b/libs/input/tests/InputConsumerFilteredResampling_test.cpp @@ -0,0 +1,218 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { +namespace { + +using std::chrono::nanoseconds; + +using ::testing::AllOf; +using ::testing::Matcher; + +const int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN; +const int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE; + +struct Pointer { + int32_t id{0}; + ToolType toolType{ToolType::FINGER}; + float x{0.0f}; + float y{0.0f}; + bool isResampled{false}; + + PointerBuilder asPointerBuilder() const { + return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled); + } +}; + +} // namespace + +class InputConsumerFilteredResamplingTest : public ::testing::Test, public InputConsumerCallbacks { +protected: + InputConsumerFilteredResamplingTest() + : mClientTestChannel{std::make_shared("TestChannel")}, + mLooper{sp::make(/*allowNonCallbacks=*/false)} { + Looper::setForThread(mLooper); + mConsumer = std::make_unique< + InputConsumerNoResampling>(mClientTestChannel, mLooper, *this, []() { + return std::make_unique(/*minCutoffFreq=*/4.7, /*beta=*/0.01); + }); + } + + void invokeLooperCallback() const { + sp callback; + ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr, + /*events=*/nullptr, &callback, /*data=*/nullptr)); + ASSERT_NE(callback, nullptr); + callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr); + } + + void assertOnBatchedInputEventPendingWasCalled() { + ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL) + << "onBatchedInputEventPending was not called"; + --mOnBatchedInputEventPendingInvocationCount; + } + + void assertReceivedMotionEvent(const Matcher& matcher) { + ASSERT_TRUE(!mMotionEvents.empty()) << "No motion events were received"; + std::unique_ptr motionEvent = std::move(mMotionEvents.front()); + mMotionEvents.pop(); + ASSERT_NE(motionEvent, nullptr) << "The consumed motion event must not be nullptr"; + EXPECT_THAT(*motionEvent, matcher); + } + + InputMessage nextPointerMessage(nanoseconds eventTime, int32_t action, const Pointer& pointer); + + std::shared_ptr mClientTestChannel; + sp mLooper; + std::unique_ptr mConsumer; + + // Batched input events + std::queue> mKeyEvents; + std::queue> mMotionEvents; + std::queue> mFocusEvents; + std::queue> mCaptureEvents; + std::queue> mDragEvents; + std::queue> mTouchModeEvents; + +private: + // InputConsumer callbacks + void onKeyEvent(std::unique_ptr event, uint32_t seq) override { + mKeyEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, /*handled=*/true); + } + + void onMotionEvent(std::unique_ptr event, uint32_t seq) override { + mMotionEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, /*handled=*/true); + } + + void onBatchedInputEventPending(int32_t pendingBatchSource) override { + if (!mConsumer->probablyHasInput()) { + ADD_FAILURE() << "Should deterministically have input because there is a batch"; + } + ++mOnBatchedInputEventPendingInvocationCount; + } + + void onFocusEvent(std::unique_ptr event, uint32_t seq) override { + mFocusEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, /*handled=*/true); + } + + void onCaptureEvent(std::unique_ptr event, uint32_t seq) override { + mCaptureEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, /*handled=*/true); + } + + void onDragEvent(std::unique_ptr event, uint32_t seq) override { + mDragEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, /*handled=*/true); + } + + void onTouchModeEvent(std::unique_ptr event, uint32_t seq) override { + mTouchModeEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, /*handled=*/true); + } + + uint32_t mLastSeq{0}; + size_t mOnBatchedInputEventPendingInvocationCount{0}; +}; + +InputMessage InputConsumerFilteredResamplingTest::nextPointerMessage(nanoseconds eventTime, + int32_t action, + const Pointer& pointer) { + ++mLastSeq; + return InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq} + .eventTime(eventTime.count()) + .source(AINPUT_SOURCE_TOUCHSCREEN) + .action(action) + .pointer(pointer.asPointerBuilder()) + .build(); +} + +TEST_F(InputConsumerFilteredResamplingTest, NeighboringTimestampsDoNotResultInZeroDivision) { + mClientTestChannel->enqueueMessage( + nextPointerMessage(0ms, ACTION_DOWN, Pointer{.x = 0.0f, .y = 0.0f})); + + invokeLooperCallback(); + + assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithSampleCount(1))); + + const std::chrono::nanoseconds initialTime{56'821'700'000'000}; + + mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 4'929'000ns, ACTION_MOVE, + Pointer{.x = 1.0f, .y = 1.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 9'352'000ns, ACTION_MOVE, + Pointer{.x = 2.0f, .y = 2.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 14'531'000ns, ACTION_MOVE, + Pointer{.x = 3.0f, .y = 3.0f})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(initialTime.count() + 18'849'395 /*ns*/); + + assertOnBatchedInputEventPendingWasCalled(); + // Three samples are expected. The first two of the batch, and the resampled one. The + // coordinates of the resampled sample are hardcoded because the matcher requires them. However, + // the primary intention here is to check that the last sample is resampled. + assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithSampleCount(3), + WithSample(/*sampleIndex=*/2, + Sample{initialTime + 13'849'395ns, + {PointerArgs{.x = 1.3286f, + .y = 1.3286f, + .isResampled = true}}}))); + + mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 20'363'000ns, ACTION_MOVE, + Pointer{.x = 4.0f, .y = 4.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 25'745'000ns, ACTION_MOVE, + Pointer{.x = 5.0f, .y = 5.0f})); + // This sample is part of the stream of messages, but should not be consumed because its + // timestamp is greater than the ajusted frame time. + mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 31'337'000ns, ACTION_MOVE, + Pointer{.x = 6.0f, .y = 6.0f})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(initialTime.count() + 35'516'062 /*ns*/); + + assertOnBatchedInputEventPendingWasCalled(); + // Four samples are expected because the last sample of the previous batch was not consumed. + assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithSampleCount(4))); + + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/5, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/6, /*handled=*/true); +} + +} // namespace android diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp index 270e789c84..8645508ea7 100644 --- a/libs/input/tests/OneEuroFilter_test.cpp +++ b/libs/input/tests/OneEuroFilter_test.cpp @@ -98,7 +98,10 @@ protected: std::vector filteredSignal; for (const Sample& sample : signal) { filteredSignal.push_back( - Sample{sample.timestamp, mFilter.filter(sample.timestamp, sample.value)}); + Sample{sample.timestamp, + mFilter.filter(std::chrono::duration_cast( + sample.timestamp), + sample.value)}); } return filteredSignal; } diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h index 3589de599f..de96600f66 100644 --- a/libs/input/tests/TestEventMatchers.h +++ b/libs/input/tests/TestEventMatchers.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -150,14 +151,18 @@ public: ++pointerIndex) { const PointerCoords& pointerCoords = *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex)); - if ((pointerCoords.getX() != mSample.pointers[pointerIndex].x) || - (pointerCoords.getY() != mSample.pointers[pointerIndex].y)) { + + if ((std::abs(pointerCoords.getX() - mSample.pointers[pointerIndex].x) > + MotionEvent::ROUNDING_PRECISION) || + (std::abs(pointerCoords.getY() - mSample.pointers[pointerIndex].y) > + MotionEvent::ROUNDING_PRECISION)) { *os << "sample coordinates mismatch at pointer index " << pointerIndex << ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY() << ") expected: (" << mSample.pointers[pointerIndex].x << ", " << mSample.pointers[pointerIndex].y << ")"; return false; } + if (motionEvent.isResampled(pointerIndex, mSampleIndex) != mSample.pointers[pointerIndex].isResampled) { *os << "resampling flag mismatch. sample: " -- cgit v1.2.3-59-g8ed1b From 83c6f9eed4bd2a10a7d73a3a0ab92766dfd8867b Mon Sep 17 00:00:00 2001 From: Lloyd Pique Date: Wed, 20 Nov 2024 18:06:07 -0800 Subject: Easier ftl::Flags construction As demonstrated by the existing test, while it is possible to construct a ftl::Flags from multiple values, it requires an extra `using namespace ftl::flag_operators` or alternatively a `using ftl::flag_operators::operator|` to implicitly construct a `ftl::Flags` type. But there is an easier way -- allow implicit construction from a `std::initializer_list`. This means as an alternative to: using namespace ftl::flag_operators; ftl::Flags x = E::A | E::B; ... one can instead write: ftl::Flags x = {E::A, E::B}; ... and achieve the same initial value. This change adds the new constructor overload. Assignment from an initializer list automatically works, without having to define a explicit `operator=(std::initializer_list)`. Instead the copy constuctor is used. As a useful side effect, you can now also clear the flags by assigning an empty initializer list: ftl::Flags x; x = {}; // Clears x to zero Bug: 185536303 Flag: EXEMPT New library code Test: atest ftl_test Change-Id: I1de7857c93ebef4fc5e6157aac9cf162b49943e1 --- include/ftl/flags.h | 10 ++++++++++ libs/ftl/flags_test.cpp | 14 +++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/include/ftl/flags.h b/include/ftl/flags.h index dbe3148fc5..a2a22ebebe 100644 --- a/include/ftl/flags.h +++ b/include/ftl/flags.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ class Flags { public: constexpr Flags(F f) : mFlags(static_cast(f)) {} + constexpr Flags(std::initializer_list fs) : mFlags(combine(fs)) {} constexpr Flags() : mFlags(0) {} constexpr Flags(const Flags& f) : mFlags(f.mFlags) {} @@ -197,6 +199,14 @@ public: private: U mFlags; + static constexpr U combine(std::initializer_list fs) { + U result = 0; + for (const F f : fs) { + result |= static_cast(f); + } + return result; + } + static void appendFlag(std::string& str, const std::string_view& flag, bool& first) { if (first) { first = false; diff --git a/libs/ftl/flags_test.cpp b/libs/ftl/flags_test.cpp index 1279d1147d..bb43e8d2a3 100644 --- a/libs/ftl/flags_test.cpp +++ b/libs/ftl/flags_test.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include namespace android::test { @@ -59,6 +59,18 @@ TEST(Flags, All) { ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); } +TEST(Flags, ImplicitConstructionAndAssignmentFromInitializerList) { + Flags flags = {TestFlags::ONE, TestFlags::THREE}; + ASSERT_TRUE(flags.test(TestFlags::ONE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); + ASSERT_TRUE(flags.test(TestFlags::THREE)); + + flags = {}; + ASSERT_FALSE(flags.test(TestFlags::ONE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); + ASSERT_FALSE(flags.test(TestFlags::THREE)); +} + TEST(Flags, DefaultConstructor_hasNoFlagsSet) { Flags flags; ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); -- cgit v1.2.3-59-g8ed1b From c1beabff3df7e5962c2c2d8add7be4a71695b6da Mon Sep 17 00:00:00 2001 From: Sally Qi Date: Tue, 19 Nov 2024 19:33:57 -0800 Subject: [LUT shader] add CIE_Y shader - Use D65 illuminant Bug: 358422255 Test: android.view.surfacecontrol.cts.SurfaceControlTest#testSurfaceTransaction_setLuts_1DLut_withCIEy Flag: EXEMPT no flag needed Change-Id: Icfae89bcd6538d6c0c0a9ba1bce2f1dba8776b8d --- libs/gui/aidl/android/gui/LutProperties.aidl | 2 +- libs/renderengine/skia/SkiaRenderEngine.cpp | 1 + libs/renderengine/skia/filters/LutShader.cpp | 59 +++++++++++++++++++++++++--- libs/renderengine/skia/filters/LutShader.h | 5 ++- 4 files changed, 58 insertions(+), 9 deletions(-) (limited to 'libs') diff --git a/libs/gui/aidl/android/gui/LutProperties.aidl b/libs/gui/aidl/android/gui/LutProperties.aidl index 87b878c1ca..84c7013cda 100644 --- a/libs/gui/aidl/android/gui/LutProperties.aidl +++ b/libs/gui/aidl/android/gui/LutProperties.aidl @@ -27,6 +27,6 @@ parcelable LutProperties { int size; @Backing(type="int") - enum SamplingKey { RGB, MAX_RGB } + enum SamplingKey { RGB, MAX_RGB, CIE_Y } SamplingKey[] samplingKeys; } \ No newline at end of file diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index a93f6c36ff..433f4a1e6a 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -545,6 +545,7 @@ sk_sp SkiaRenderEngine::createRuntimeEffectShader( if (graphicBuffer && parameters.layer.luts) { shader = mLutShader.lutShader(shader, parameters.layer.luts, + parameters.layer.sourceDataspace, toSkColorSpace(parameters.outputDataSpace)); } diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp index 1e43ff3cd3..5e9dfbba3e 100644 --- a/libs/renderengine/skia/filters/LutShader.cpp +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -15,11 +15,13 @@ */ #include "LutShader.h" +#include #include #include #include #include #include +#include #include "include/core/SkColorSpace.h" #include "src/core/SkColorFilterPriv.h" @@ -36,6 +38,8 @@ static const SkString kShader = SkString(R"( uniform int size; uniform int key; uniform int dimension; + uniform vec3 luminanceCoefficients; // for CIE_Y + vec4 main(vec2 xy) { float4 rgba = image.eval(xy); float3 linear = toLinearSrgb(rgba.rgb); @@ -51,12 +55,16 @@ static const SkString kShader = SkString(R"( return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a); // MAX_RGB } else if (key == 1) { - float4 rgba = image.eval(xy); - float3 linear = toLinearSrgb(rgba.rgb); float maxRGB = max(linear.r, max(linear.g, linear.b)); float index = maxRGB * float(size - 1); float gain = lut.eval(vec2(index, 0.0) + 0.5).r; return float4(linear * gain, rgba.a); + // CIE_Y + } else if (key == 2) { + float y = dot(linear, luminanceCoefficients) / 3.0; + float index = y * float(size - 1); + float gain = lut.eval(vec2(index, 0.0) + 0.5).r; + return float4(linear * gain, rgba.a); } } else if (dimension == 3) { if (key == 0) { @@ -110,11 +118,37 @@ static const SkString kShader = SkString(R"( return rgba; })"); +// same as shader::toColorSpace function +// TODO: put this function in a general place +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(); + 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_BT470M: + case HAL_DATASPACE_STANDARD_FILM: + case HAL_DATASPACE_STANDARD_UNSPECIFIED: + default: + return ColorSpace::sRGB(); + } +} + sk_sp LutShader::generateLutShader(sk_sp input, const std::vector& buffers, const int32_t offset, const int32_t length, const int32_t dimension, const int32_t size, - const int32_t samplingKey) { + const int32_t samplingKey, + ui::Dataspace srcDataspace) { SFTRACE_NAME("lut shader"); std::vector buffer(length * 4); // 4 is for RGBA auto d = static_cast(dimension); @@ -133,12 +167,16 @@ sk_sp LutShader::generateLutShader(sk_sp input, } } /** - * 1D Lut(rgba) + * 1D Lut RGB/MAX_RGB * (R0, 0, 0, 0) * (R1, 0, 0, 0) + * + * 1D Lut CIE_Y + * (Y0, 0, 0, 0) + * (Y1, 0, 0, 0) * ... * - * 3D Lut + * 3D Lut MAX_RGB * (R0, G0, B0, 0) * (R1, G1, B1, 0) * ... @@ -162,6 +200,14 @@ sk_sp LutShader::generateLutShader(sk_sp input, const int uSize = static_cast(size); const int uKey = static_cast(samplingKey); const int uDimension = static_cast(dimension); + if (static_cast(samplingKey) == LutProperties::SamplingKey::CIE_Y) { + // Use predefined colorspaces of input dataspace so that we can get D65 illuminant + mat3 toXYZMatrix(toColorSpace(srcDataspace).getRGBtoXYZ()); + mBuilder->uniform("luminanceCoefficients") = + SkV3{toXYZMatrix[0][1], toXYZMatrix[1][1], toXYZMatrix[2][1]}; + } else { + mBuilder->uniform("luminanceCoefficients") = SkV3{1.f, 1.f, 1.f}; + } mBuilder->uniform("size") = uSize; mBuilder->uniform("key") = uKey; mBuilder->uniform("dimension") = uDimension; @@ -170,6 +216,7 @@ sk_sp LutShader::generateLutShader(sk_sp input, sk_sp LutShader::lutShader(sk_sp& input, std::shared_ptr displayLuts, + ui::Dataspace srcDataspace, sk_sp outColorSpace) { if (mBuilder == nullptr) { const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader); @@ -218,7 +265,7 @@ sk_sp LutShader::lutShader(sk_sp& input, } input = generateLutShader(input, buffers, offsets[i], bufferSizePerLut, lutProperties[i].dimension, lutProperties[i].size, - lutProperties[i].samplingKey); + lutProperties[i].samplingKey, srcDataspace); } auto colorXformLutToDst = diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h index ce3e0592a1..7c62fcae08 100644 --- a/libs/renderengine/skia/filters/LutShader.h +++ b/libs/renderengine/skia/filters/LutShader.h @@ -21,6 +21,7 @@ #include #include +#include namespace android { namespace renderengine { @@ -29,13 +30,13 @@ namespace skia { class LutShader { public: sk_sp lutShader(sk_sp& input, std::shared_ptr displayLuts, - sk_sp outColorSpace); + ui::Dataspace srcDataspace, sk_sp outColorSpace); private: sk_sp generateLutShader(sk_sp input, const std::vector& buffers, const int32_t offset, const int32_t length, const int32_t dimension, const int32_t size, - const int32_t samplingKey); + const int32_t samplingKey, ui::Dataspace srcDataspace); std::unique_ptr mBuilder; }; -- cgit v1.2.3-59-g8ed1b From c6acad1f9efccfe3cdb85132aca078a5445cf2d1 Mon Sep 17 00:00:00 2001 From: Noelle Scobie Date: Wed, 20 Nov 2024 21:53:20 -0500 Subject: Add r/w opt-in rollout flag for RenderEngine on Graphite This flag will allow some devices to opt in to RE-Graphite as a preview rollout progresses. Making it a R/W flag will allow for A/B comparison. Test: manual validation of aconfig flag / sysprop combos Bug: b/293371537 Flag: com.android.graphics.surfaceflinger.flags.graphite_renderengine_preview_rollout Change-Id: I30a59986d1da2dc8355c2b3cf2da436c6cc64120 --- libs/renderengine/include/renderengine/RenderEngine.h | 8 ++++++++ services/surfaceflinger/SurfaceFlinger.cpp | 15 ++++++++++----- services/surfaceflinger/common/FlagManager.cpp | 2 ++ .../surfaceflinger/common/include/common/FlagManager.h | 1 + services/surfaceflinger/surfaceflinger_flags_new.aconfig | 7 +++++++ 5 files changed, 28 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 0fd982e812..95c4d033e2 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -37,6 +37,14 @@ */ #define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" +/** + * Allows opting particular devices into an initial preview rollout of RenderEngine on Graphite. + * + * Only applicable within SurfaceFlinger, and if relevant aconfig flags are enabled. + */ +#define PROPERTY_DEBUG_RENDERENGINE_GRAPHITE_PREVIEW_OPTIN \ + "debug.renderengine.graphite_preview_optin" + /** * Turns on recording of skia commands in SkiaGL version of the RE. This property * defines number of milliseconds for the recording to take place. A non zero value diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1c61b11cf9..3da0dc9d44 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -797,6 +797,12 @@ void SurfaceFlinger::bootFinished() { })); } +bool shouldUseGraphiteIfCompiledAndSupported() { + return FlagManager::getInstance().graphite_renderengine() || + (FlagManager::getInstance().graphite_renderengine_preview_rollout() && + base::GetBoolProperty(PROPERTY_DEBUG_RENDERENGINE_GRAPHITE_PREVIEW_OPTIN, false)); +} + void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) { char prop[PROPERTY_VALUE_MAX]; property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); @@ -825,14 +831,13 @@ void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& bui // is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :) #if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \ COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE - const bool useGraphite = FlagManager::getInstance().graphite_renderengine() && + const bool useGraphite = shouldUseGraphiteIfCompiledAndSupported() && renderengine::RenderEngine::canSupport(kVulkan); #else const bool useGraphite = false; - if (FlagManager::getInstance().graphite_renderengine()) { - ALOGE("RenderEngine's Graphite Skia backend was requested with the " - "debug.renderengine.graphite system property, but it is not compiled in this " - "build! Falling back to Ganesh backend selection logic."); + if (shouldUseGraphiteIfCompiledAndSupported()) { + ALOGE("RenderEngine's Graphite Skia backend was requested, but it is not compiled in " + "this build! Falling back to Ganesh backend selection logic."); } #endif const bool useVulkan = useGraphite || diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index bd151beaba..f257c7cf10 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -115,6 +115,7 @@ void FlagManager::dump(std::string& result) const { DUMP_ACONFIG_FLAG(adpf_gpu_sf); DUMP_ACONFIG_FLAG(adpf_native_session_manager); DUMP_ACONFIG_FLAG(adpf_use_fmq_channel); + DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout); /// Trunk stable readonly flags /// DUMP_ACONFIG_FLAG(adpf_fmq_sf); @@ -265,6 +266,7 @@ FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg"); FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "") FLAG_MANAGER_ACONFIG_FLAG(adpf_gpu_sf, "") FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, ""); +FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, ""); /// Trunk stable server (R/W) flags from outside SurfaceFlinger /// FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os) diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index fd2306fe0e..a461627eef 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -53,6 +53,7 @@ public: bool adpf_use_fmq_channel() const; bool adpf_native_session_manager() const; bool adpf_use_fmq_channel_fixed() const; + bool graphite_renderengine_preview_rollout() const; /// Trunk stable readonly flags /// bool adpf_fmq_sf() const; diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index 34a935a223..2c44e4ce0d 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -172,6 +172,13 @@ flag { } } # frame_rate_category_mrr +flag { + name: "graphite_renderengine_preview_rollout" + namespace: "core_graphics" + description: "R/W flag to enable Skia's Graphite Vulkan backend in RenderEngine, IF it is already compiled with force_compile_graphite_renderengine, AND the debug.renderengine.graphite_preview_optin sysprop is set to true." + bug: "293371537" +} # graphite_renderengine_preview_rollout + flag { name: "latch_unsignaled_with_auto_refresh_changed" namespace: "core_graphics" -- cgit v1.2.3-59-g8ed1b From 0cc2e2cd828b7a0a33132e860289e6481df24d59 Mon Sep 17 00:00:00 2001 From: Carlos Martinez Romero Date: Mon, 14 Oct 2024 10:16:56 -0700 Subject: Use view::Surface instead of IGBPs in OutputConfiguration We are currently limitig the use of IGBPs outside of libgui to allow for further development of bufferqueues without external breakages. More information at go/warren-buffers. BYPASS_IGBP_IGBC_API_REASON: this CL is part of the migration. Bug: 342197849 Test: atest android.hardware.cts.CameraTest Flag: com.android.graphics.libgui.flags.wb_libcameraservice Change-Id: I299be522d47849ca67bcbf22519535a39343ed37 --- libs/gui/Android.bp | 1 + libs/gui/Flags.cpp | 73 +++++++++++++++++++++++++++++++++++++ libs/gui/include/gui/Flags.h | 26 +++++++++++-- libs/gui/include/gui/view/Surface.h | 16 ++++++++ libs/gui/view/Surface.cpp | 4 ++ 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 libs/gui/Flags.cpp (limited to 'libs') diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 1e33abbdea..052b519db6 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -264,6 +264,7 @@ filegroup { "DisplayEventDispatcher.cpp", "DisplayEventReceiver.cpp", "FenceMonitor.cpp", + "Flags.cpp", "GLConsumer.cpp", "IConsumerListener.cpp", "IGraphicBufferConsumer.cpp", diff --git a/libs/gui/Flags.cpp b/libs/gui/Flags.cpp new file mode 100644 index 0000000000..85ee2cddad --- /dev/null +++ b/libs/gui/Flags.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +namespace android { +namespace flagtools { +sp surfaceToSurfaceType(const sp& surface) { +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + return surface; +#else + return surface->getIGraphicBufferProducer(); +#endif +} + +sp surfaceTypeToIGBP(const sp& surface) { +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + return surface->getIGraphicBufferProducer(); +#else + return surface; +#endif +} + +bool isSurfaceTypeValid(const sp& surface) { +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + return Surface::isValid(surface); +#else + return surface != nullptr; +#endif +} + +ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface) { +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + return surface; +#else + return surface.graphicBufferProducer; +#endif +} + +ParcelableSurfaceType convertSurfaceTypeToParcelable(sp surface) { +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + return view::Surface::fromSurface(surface); +#else + return surface; +#endif +} + +sp convertParcelableSurfaceTypeToSurface(const ParcelableSurfaceType& surface) { +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + return surface.toSurface(); +#else + return surface; +#endif +} + +} // namespace flagtools +} // namespace android \ No newline at end of file diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h index 34350d2c91..845bc54c71 100644 --- a/libs/gui/include/gui/Flags.h +++ b/libs/gui/include/gui/Flags.h @@ -17,8 +17,15 @@ #pragma once #include -#include -#include +#include + +namespace android { + +class IGraphicBufferProducer; +class Surface; +namespace view { +class Surface; +} #define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \ (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \ @@ -31,6 +38,19 @@ #if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES typedef android::Surface SurfaceType; +typedef android::view::Surface ParcelableSurfaceType; #else typedef android::IGraphicBufferProducer SurfaceType; -#endif \ No newline at end of file +typedef android::sp ParcelableSurfaceType; +#endif + +namespace flagtools { +sp surfaceToSurfaceType(const sp& surface); +ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface); +sp surfaceTypeToIGBP(const sp& surface); +bool isSurfaceTypeValid(const sp& surface); +ParcelableSurfaceType convertSurfaceTypeToParcelable(sp surface); +sp convertParcelableSurfaceTypeToSurface(const ParcelableSurfaceType& surface); +} // namespace flagtools + +} // namespace android diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h index 7c762d3869..bd8704ddc7 100644 --- a/libs/gui/include/gui/view/Surface.h +++ b/libs/gui/include/gui/view/Surface.h @@ -54,6 +54,22 @@ class Surface : public Parcelable { sp toSurface() const; status_t getUniqueId(/* out */ uint64_t* id) const; + + bool isEmpty() const; + + bool operator==(const Surface& other) const { + return graphicBufferProducer == other.graphicBufferProducer; + } + bool operator!=(const Surface& other) const { return !(*this == other); } + bool operator==(const sp other) const { + if (other == nullptr) return graphicBufferProducer == nullptr; + return graphicBufferProducer == other->getIGraphicBufferProducer(); + } + bool operator!=(const sp other) const { return !(*this == other); } + bool operator<(const Surface& other) const { + return graphicBufferProducer < other.graphicBufferProducer; + } + bool operator>(const Surface& other) const { return other < *this; } #endif virtual status_t writeToParcel(Parcel* parcel) const override; diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp index 9f57923886..2cf7081ede 100644 --- a/libs/gui/view/Surface.cpp +++ b/libs/gui/view/Surface.cpp @@ -151,6 +151,10 @@ status_t Surface::getUniqueId(uint64_t* out_id) const { } return OK; } + +bool Surface::isEmpty() const { + return graphicBufferProducer == nullptr; +} #endif std::string Surface::toString() const { -- cgit v1.2.3-59-g8ed1b From 75885d9f92d19a2d7778a33f19a0c15fd5e13d14 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 20 Nov 2024 22:56:20 +0000 Subject: readCString: implemented with readInplace All read logic must go through and be validated in the same few places. Bug: 376674798 Test: binderClearBufTest covers this, and in presubmit Change-Id: Icc0ade84b671ecd3026069d8f672ff254d58e995 --- libs/binder/Parcel.cpp | 4 +--- libs/binder/tests/binderParcelUnitTest.cpp | 32 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 96d821e196..a5f416f1ba 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -2223,9 +2223,7 @@ const char* Parcel::readCString() const const char* eos = reinterpret_cast(memchr(str, 0, avail)); if (eos) { const size_t len = eos - str; - mDataPos += pad_size(len+1); - ALOGV("readCString Setting data pos of %p to %zu", this, mDataPos); - return str; + return static_cast(readInplace(len + 1)); } } return nullptr; diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp index 32a70e5b11..6259d9d2d2 100644 --- a/libs/binder/tests/binderParcelUnitTest.cpp +++ b/libs/binder/tests/binderParcelUnitTest.cpp @@ -33,6 +33,38 @@ using android::String8; using android::binder::Status; using android::binder::unique_fd; +static void checkCString(const char* str) { + for (size_t i = 0; i < 3; i++) { + Parcel p; + + for (size_t j = 0; j < i; j++) p.writeInt32(3); + + p.writeCString(str); + int32_t pos = p.dataPosition(); + + p.setDataPosition(0); + + for (size_t j = 0; j < i; j++) p.readInt32(); + const char* str2 = p.readCString(); + + ASSERT_EQ(std::string(str), str2); + ASSERT_EQ(pos, p.dataPosition()); + } +} + +TEST(Parcel, TestReadCString) { + // we should remove the *CString APIs, but testing them until + // they are deleted. + checkCString(""); + checkCString("a"); + checkCString("\n"); + checkCString("32"); + checkCString("321"); + checkCString("3210"); + checkCString("3210b"); + checkCString("123434"); +} + TEST(Parcel, NonNullTerminatedString8) { String8 kTestString = String8("test-is-good"); -- cgit v1.2.3-59-g8ed1b From eb63bbfd16ed9732be13d2ea6699edc6d7e4b545 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 13 Nov 2024 15:13:29 -0800 Subject: Do not crash when server channel has closed If the publisher has been destroyed (or, equivalently, if the server channel was closed), the consumer should not crash. Instead, we should allow consumer to exit gracefully. In this CL, we specifically check for DEAD_OBJECT and drain the queue, printing all of the events that will never be delivered to the publisher for useful debugging purposes. Bug: 305165753 Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST Flag: EXEMPT bugfix Change-Id: I00973ebb87e0f59bfd3c0f58edf7355b28988c15 --- libs/input/InputConsumerNoResampling.cpp | 15 +++++++ .../InputPublisherAndConsumerNoResampling_test.cpp | 48 +++++++++++++++------- 2 files changed, 48 insertions(+), 15 deletions(-) (limited to 'libs') diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index 2c0f77a814..cd8582182a 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -169,6 +169,12 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime; return msg; } + +std::ostream& operator<<(std::ostream& out, const InputMessage& msg) { + out << ftl::enum_string(msg.header.type); + return out; +} + } // namespace // --- InputConsumerNoResampling --- @@ -272,6 +278,15 @@ void InputConsumerNoResampling::processOutboundEvents() { return; // try again later } + if (result == DEAD_OBJECT) { + // If there's no one to receive events in the channel, there's no point in sending them. + // Drop all outbound events. + LOG(INFO) << "Channel " << mChannel->getName() << " died. Dropping outbound event " + << outboundMsg; + mOutboundQueue.pop(); + setFdEvents(0); + continue; + } // Some other error. Give up LOG(FATAL) << "Failed to send outbound event on channel '" << mChannel->getName() << "'. status=" << statusToString(result) << "(" << result << ")"; diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index 39bb841c0a..1dadae98e4 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -319,6 +319,8 @@ protected: protected: // Interaction with the looper thread + void blockLooper(); + void unblockLooper(); enum class LooperMessage : int { CALL_PROBABLY_HAS_INPUT, CREATE_CONSUMER, @@ -389,6 +391,26 @@ private: }; }; +void InputPublisherAndConsumerNoResamplingTest::blockLooper() { + { + std::scoped_lock l(mLock); + mLooperMayProceed = false; + } + sendMessage(LooperMessage::BLOCK_LOOPER); + { + std::unique_lock l(mLock); + mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; }); + } +} + +void InputPublisherAndConsumerNoResamplingTest::unblockLooper() { + { + std::scoped_lock l(mLock); + mLooperMayProceed = true; + } + mNotifyLooperMayProceed.notify_all(); +} + void InputPublisherAndConsumerNoResamplingTest::sendMessage(LooperMessage message) { Message msg{ftl::to_underlying(message)}; mLooper->sendMessage(mMessageHandler, msg); @@ -600,15 +622,7 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMu std::queue publishedSequenceNumbers; // Block Looper to increase the chance of batching events - { - std::scoped_lock l(mLock); - mLooperMayProceed = false; - } - sendMessage(LooperMessage::BLOCK_LOOPER); - { - std::unique_lock l(mLock); - mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; }); - } + blockLooper(); uint32_t firstSampleId; for (size_t i = 0; i < nSamples; ++i) { @@ -629,12 +643,7 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMu std::vector singleSampledMotionEvents; - // Unblock Looper - { - std::scoped_lock l(mLock); - mLooperMayProceed = true; - } - mNotifyLooperMayProceed.notify_all(); + unblockLooper(); // We have no control over the socket behavior, so the consumer can receive // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a @@ -809,6 +818,15 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeTouchModeEvent( verifyFinishedSignal(*mPublisher, seq, publishTime); } +/** + * If the publisher has died, consumer should not crash when trying to send an outgoing message. + */ +TEST_F(InputPublisherAndConsumerNoResamplingTest, ConsumerWritesAfterPublisherDies) { + mPublisher.reset(); // The publisher has died + mReportTimelineArgs.emplace(/*inputEventId=*/10, /*gpuCompletedTime=*/20, /*presentTime=*/30); + sendMessage(LooperMessage::CALL_REPORT_TIMELINE); +} + TEST_F(InputPublisherAndConsumerNoResamplingTest, SendTimeline) { const int32_t inputEventId = 20; const nsecs_t gpuCompletedTime = 30; -- cgit v1.2.3-59-g8ed1b From 6ac21eccd2f7d9912fe107c2a32bd3866f08c10d Mon Sep 17 00:00:00 2001 From: Rachel Lee Date: Tue, 26 Nov 2024 19:23:42 +0000 Subject: Revert "Add new ANativeWindow_setFrameRateParams API" Revert submission 29890736-anativewindow-setframerateparams Reason for revert: removing API to be landed in another release. Reverted changes: /q/submissionid:29890736-anativewindow-setframerateparams Change-Id: I366030e9bcb4b468e728cf425c1c22360700fe71 --- libs/nativewindow/ANativeWindow.cpp | 10 --- libs/nativewindow/include/android/native_window.h | 76 +---------------------- libs/nativewindow/include/system/window.h | 17 ----- libs/nativewindow/libnativewindow.map.txt | 1 - 4 files changed, 3 insertions(+), 101 deletions(-) (limited to 'libs') diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index ac3a832168..f97eed5db3 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -263,16 +263,6 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy); } -int32_t ANativeWindow_setFrameRateParams( - ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate, - ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) { - if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { - return -EINVAL; - } - return native_window_set_frame_rate_params(window, desiredMinRate, desiredMaxRate, - fixedSourceRate, changeFrameRateStrategy); -} - /************************************************************************************************** * vndk-stable **************************************************************************************************/ diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index c53d11eee1..be6623ee75 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -33,8 +33,8 @@ #ifndef ANDROID_NATIVE_WINDOW_H #define ANDROID_NATIVE_WINDOW_H -#include #include +#include #include #include @@ -282,7 +282,7 @@ int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_ void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30); /** Change frame rate strategy value for ANativeWindow_setFrameRate. */ -typedef enum ANativeWindow_ChangeFrameRateStrategy : int8_t { +enum ANativeWindow_ChangeFrameRateStrategy { /** * Change the frame rate only if the transition is going to be seamless. */ @@ -292,7 +292,7 @@ typedef enum ANativeWindow_ChangeFrameRateStrategy : int8_t { * i.e. with visual interruptions for the user. */ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1 -} ANativeWindow_ChangeFrameRateStrategy __INTRODUCED_IN(31); +} __INTRODUCED_IN(31); /** * Sets the intended frame rate for this window. @@ -344,76 +344,6 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa int8_t compatibility, int8_t changeFrameRateStrategy) __INTRODUCED_IN(31); -/** - * Sets the intended frame rate for this window. - * - * On devices that are capable of running the display at different frame rates, - * the system may choose a display refresh rate to better match this surface's frame - * rate. Usage of this API won't introduce frame rate throttling, or affect other - * aspects of the application's frame production pipeline. However, because the system - * may change the display refresh rate, calls to this function may result in changes - * to Choreographer callback timings, and changes to the time interval at which the - * system releases buffers back to the application. - * - * Note that this only has an effect for surfaces presented on the display. If this - * surface is consumed by something other than the system compositor, e.g. a media - * codec, this call has no effect. - * - * You can register for changes in the refresh rate using - * \a AChoreographer_registerRefreshRateCallback. - * - * See ANativeWindow_clearFrameRate(). - * - * Available since API level 36. - * - * \param window pointer to an ANativeWindow object. - * - * \param desiredMinRate The desired minimum frame rate (inclusive) for the window, specifying that - * the surface prefers the device render rate to be at least `desiredMinRate`. - * - *

Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate. - * - *

Set `desiredMinRate` = 0 to indicate the window has no preference - * and any frame rate is acceptable. - * - *

The value should be greater than or equal to 0. - * - * \param desiredMaxRate The desired maximum frame rate (inclusive) for the window, specifying that - * the surface prefers the device render rate to be at most `desiredMaxRate`. - * - *

Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate. - * - *

Set `desiredMaxRate` = positive infinity to indicate the window has no preference - * and any frame rate is acceptable. - * - *

The value should be greater than or equal to `desiredMinRate`. - * - * \param fixedSourceRate The "fixed source" frame rate of the window if the content has an - * inherently fixed frame rate, e.g. a video that has a specific frame rate. - * - *

When the frame rate chosen for the surface is the `fixedSourceRate` or a - * multiple, the surface can render without frame pulldown, for optimal smoothness. For - * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps, - * 60 fps, 90 fps, 120 fps, and so on. - * - *

Setting the fixed source rate can also be used together with a desired - * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still - * means the window's content has a fixed frame rate of `fixedSourceRate`, but additionally - * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an - * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth - * animation on the same window which looks good when drawing within a frame rate range such as - * [`desiredMinRate`, `desiredMaxRate`] = [60,120]. - * - * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface - * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such - * as a black screen for a second or two. - * - * \return 0 for success, -EINVAL if the arguments are invalid. - */ -int32_t ANativeWindow_setFrameRateParams( - ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate, - ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36); - /** * Clears the frame rate which is set for this window. * diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 05f49ad25f..33c303ae71 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1156,23 +1156,6 @@ static inline int native_window_set_frame_rate(struct ANativeWindow* window, flo (int)compatibility, (int)changeFrameRateStrategy); } -static inline int native_window_set_frame_rate_params(struct ANativeWindow* window, - float desiredMinRate, float desiredMaxRate, - float fixedSourceRate, - int8_t changeFrameRateStrategy) { - // TODO(b/362798998): Fix plumbing to send whole params - int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT - : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; - double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE - ? fixedSourceRate - : desiredMinRate; - if (desiredMaxRate < desiredMinRate) { - return -EINVAL; - } - return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, frameRate, compatibility, - changeFrameRateStrategy); -} - struct ANativeWindowFrameTimelineInfo { // Frame Id received from ANativeWindow_getNextFrameId. uint64_t frameNumber; diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 071e3548d0..e29d5a6bb4 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -53,7 +53,6 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersTransform; ANativeWindow_setDequeueTimeout; # systemapi introduced=30 ANativeWindow_setFrameRate; # introduced=30 - ANativeWindow_setFrameRateParams; # introduced=36 ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk ANativeWindow_setSwapInterval; # llndk -- cgit v1.2.3-59-g8ed1b From 5ef1fa1b61ed3e73e450b8b4997509fe706b9fbe Mon Sep 17 00:00:00 2001 From: Manasi Navare Date: Thu, 7 Nov 2024 23:49:46 +0000 Subject: SF: Propagate Display Mode Rejection from SF to DM Create a new DisplayEventReceiver callback for onModeRejected to signal mode rejection from SF to DM so that DM can trigger a retry. Add the plumbing through DisplayEventReceiver and EventThread Bug: 374184110 Test: manual Flag: com.android.graphics.surfaceflinger.flags.display_config_error_hal Change-Id: I78515d413bdf2e6e07d6649b7b60df8ced199b19 Signed-off-by: Manasi Navare --- libs/gui/Choreographer.cpp | 4 ++++ libs/gui/DisplayEventDispatcher.cpp | 3 +++ libs/gui/include/gui/Choreographer.h | 1 + libs/gui/include/gui/DisplayEventDispatcher.h | 2 ++ libs/gui/include/gui/DisplayEventReceiver.h | 6 ++++++ services/surfaceflinger/Scheduler/EventThread.cpp | 24 ++++++++++++++++++++++ services/surfaceflinger/Scheduler/EventThread.h | 7 +++++++ services/surfaceflinger/Scheduler/Scheduler.cpp | 6 ++++++ services/surfaceflinger/Scheduler/Scheduler.h | 4 ++++ services/surfaceflinger/SurfaceFlinger.cpp | 12 ++++++++--- .../tests/unittests/mock/MockEventThread.h | 2 ++ 11 files changed, 68 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 0c8f3fa096..ba50bf83a8 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -369,6 +369,10 @@ void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32 this, to_string(displayId).c_str(), connectedLevel, maxLevel); } +void Choreographer::dispatchModeRejected(PhysicalDisplayId, int32_t) { + LOG_ALWAYS_FATAL("dispatchModeRejected was called but was never registered"); +} + void Choreographer::handleMessage(const Message& message) { switch (message.what) { case MSG_SCHEDULE_CALLBACKS: diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index c46f9c50ef..68f10f4d80 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -211,6 +211,9 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, ev.hdcpLevelsChange.connectedLevel, ev.hdcpLevelsChange.maxLevel); break; + case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION: + dispatchModeRejected(ev.header.displayId, ev.modeRejection.modeId); + break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); break; diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index 2e5aa4a893..a93ba14c57 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -127,6 +127,7 @@ private: std::vector overrides) override; void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) override; + void dispatchModeRejected(PhysicalDisplayId displayId, int32_t modeId) override; void scheduleCallbacks(); diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index 82cd50c7bd..b06ad077f4 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -68,6 +68,8 @@ private: virtual void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) = 0; + virtual void dispatchModeRejected(PhysicalDisplayId displayId, int32_t modeId) = 0; + bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, VsyncEventData* outVsyncEventData); diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 40a6e79a24..ab6a6b78f7 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -62,6 +62,7 @@ public: DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'), + DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'), DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'), DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'), DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'), @@ -96,6 +97,10 @@ public: nsecs_t vsyncPeriod __attribute__((aligned(8))); }; + struct ModeRejection { + int32_t modeId; + }; + struct FrameRateOverride { uid_t uid __attribute__((aligned(8))); float frameRateHz __attribute__((aligned(8))); @@ -117,6 +122,7 @@ public: ModeChange modeChange; FrameRateOverride frameRateOverride; HdcpLevelsChange hdcpLevelsChange; + ModeRejection modeRejection; }; }; static_assert(sizeof(Event) == 224); diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index fff4284199..c6d7160818 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include "FrameTimeline.h" #include "VSyncDispatch.h" @@ -102,6 +103,10 @@ std::string toString(const DisplayEventReceiver::Event& event) { to_string(event.header.displayId).c_str(), event.hdcpLevelsChange.connectedLevel, event.hdcpLevelsChange.maxLevel); + case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION: + return StringPrintf("ModeRejected{displayId=%s, modeId=%u}", + to_string(event.header.displayId).c_str(), + event.modeRejection.modeId); default: return "Event{}"; } @@ -186,6 +191,18 @@ DisplayEventReceiver::Event makeHdcpLevelsChange(PhysicalDisplayId displayId, }; } +DisplayEventReceiver::Event makeModeRejection(PhysicalDisplayId displayId, DisplayModeId modeId) { + return DisplayEventReceiver::Event{ + .header = + DisplayEventReceiver::Event::Header{ + .type = DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION, + .displayId = displayId, + .timestamp = systemTime(), + }, + .modeRejection.modeId = ftl::to_underlying(modeId), + }; +} + } // namespace EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid, @@ -480,6 +497,13 @@ void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t conne mCondition.notify_all(); } +void EventThread::onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) { + std::lock_guard lock(mMutex); + + mPendingEvents.push_back(makeModeRejection(displayId, modeId)); + mCondition.notify_all(); +} + // Merge lists of buffer stuffed Uids void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { std::lock_guard lock(mMutex); diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 95632c7e66..18bf41643c 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ #include #include +#include "DisplayHardware/DisplayMode.h" #include "TracedOrdinal.h" #include "VSyncDispatch.h" #include "VsyncSchedule.h" @@ -115,6 +117,9 @@ public: // called when SF changes the active mode and apps needs to be notified about the change virtual void onModeChanged(const scheduler::FrameRateMode&) = 0; + // called when SF rejects the mode change request + virtual void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) = 0; + // called when SF updates the Frame Rate Override list virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId, std::vector overrides) = 0; @@ -179,6 +184,8 @@ public: void onModeChanged(const scheduler::FrameRateMode&) override; + void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) override; + void onFrameRateOverridesChanged(PhysicalDisplayId displayId, std::vector overrides) override; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 2875650ed0..86d91d9433 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -465,6 +465,12 @@ bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRat } #pragma clang diagnostic pop +void Scheduler::onDisplayModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) { + if (hasEventThreads()) { + eventThreadFor(Cycle::Render).onModeRejected(displayId, modeId); + } +} + void Scheduler::emitModeChangeIfNeeded() { if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) { ALOGW("No mode change to emit"); diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 61c68a9473..3fdddac52a 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -36,12 +36,14 @@ #include #include #include +#include #include #include #include #include #include +#include "DisplayHardware/DisplayMode.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" #include "ISchedulerCallback.h" @@ -153,6 +155,8 @@ public: bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&, bool clearContentRequirements) EXCLUDES(mPolicyLock); + void onDisplayModeRejected(PhysicalDisplayId, DisplayModeId); + void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext); void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 97c86232f2..0c2922d0ed 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1532,9 +1532,15 @@ void SurfaceFlinger::initiateDisplayModeChanges() { constraints.seamlessRequired = false; hal::VsyncPeriodChangeTimeline outTimeline; - if (mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt), - constraints, outTimeline) != - display::DisplayModeController::ModeChangeResult::Changed) { + const auto error = + mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt), + constraints, outTimeline); + if (error != display::DisplayModeController::ModeChangeResult::Changed) { + dropModeRequest(displayId); + if (FlagManager::getInstance().display_config_error_hal() && + error == display::DisplayModeController::ModeChangeResult::Rejected) { + mScheduler->onDisplayModeRejected(displayId, desiredModeId); + } continue; } diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index c976697c08..3036fec456 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -34,6 +34,8 @@ public: MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override)); MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override)); MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override)); + MOCK_METHOD(void, onModeRejected, (PhysicalDisplayId displayId, DisplayModeId modeId), + (override)); MOCK_METHOD(void, onFrameRateOverridesChanged, (PhysicalDisplayId, std::vector), (override)); MOCK_METHOD(void, dump, (std::string&), (const, override)); -- cgit v1.2.3-59-g8ed1b From 227aef2d74a5a32547c2570a1c85f8f9c8fef4d5 Mon Sep 17 00:00:00 2001 From: Rachel Lee Date: Tue, 26 Nov 2024 11:31:08 -0800 Subject: Add GTE compatibility enum to ANativeWindow. This moves and renames the enum into ANativeWindow where it is accessible in the public NDK. Test: atest SetFrameRateTest Test: atest libsurfaceflinger_unittest Test: atest LayerHistoryIntegrationTest Bug: 380949716 Flag: EXEMPT ndk Change-Id: I5216c3ceb223f7b9a0571be14544e83d7f8859ea --- libs/gui/FrameRateUtils.cpp | 2 +- libs/gui/tests/FrameRateUtilsTest.cpp | 2 +- libs/nativewindow/include/android/native_window.h | 12 +++++++++--- libs/nativewindow/include/system/window.h | 7 +------ services/surfaceflinger/Scheduler/LayerInfo.cpp | 2 +- .../tests/unittests/LayerHistoryIntegrationTest.cpp | 4 ++-- 6 files changed, 15 insertions(+), 14 deletions(-) (limited to 'libs') diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp index 01aa7ed43c..5c4879c1bd 100644 --- a/libs/gui/FrameRateUtils.cpp +++ b/libs/gui/FrameRateUtils.cpp @@ -42,7 +42,7 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrame if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE && - compatibility != ANATIVEWINDOW_FRAME_RATE_GTE && + compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE && (!privileged || (compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT && compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) { diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp index 04bfb28f65..5d3287d373 100644 --- a/libs/gui/tests/FrameRateUtilsTest.cpp +++ b/libs/gui/tests/FrameRateUtilsTest.cpp @@ -34,7 +34,7 @@ TEST(FrameRateUtilsTest, ValidateFrameRate) { ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, "")); EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); - EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_GTE, + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); // Privileged APIs. diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 6f816bf614..ed3e8c1a62 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -243,8 +243,7 @@ enum ANativeWindow_FrameRateCompatibility { * There are no inherent restrictions on the frame rate of this window. When * the system selects a frame rate other than what the app requested, the * app will be able to run at the system frame rate without requiring pull - * down. This value should be used when displaying game content, UIs, and - * anything that isn't video. + * down. This value should be used when displaying game content. */ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0, /** @@ -256,7 +255,14 @@ enum ANativeWindow_FrameRateCompatibility { * stuttering) than it would be if the system had chosen the app's requested * frame rate. This value should be used for video content. */ - ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1 + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1, + + /** + * The window requests a frame rate that is greater than or equal to the specified frame rate. + * This value should be used for UIs, animations, scrolling, and anything that is not a game + * or video. + */ + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE = 2 }; /** diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 33c303ae71..f669f7781e 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1060,12 +1060,7 @@ enum { /** * This surface will vote for the minimum refresh rate. */ - ANATIVEWINDOW_FRAME_RATE_MIN, - - /** - * The surface requests a frame rate that is greater than or equal to `frameRate`. - */ - ANATIVEWINDOW_FRAME_RATE_GTE + ANATIVEWINDOW_FRAME_RATE_MIN }; /* diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index ff1926e03f..356c30ed5f 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -504,7 +504,7 @@ FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compati return FrameRateCompatibility::Exact; case ANATIVEWINDOW_FRAME_RATE_MIN: return FrameRateCompatibility::Min; - case ANATIVEWINDOW_FRAME_RATE_GTE: + case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE: return FrameRateCompatibility::Gte; case ANATIVEWINDOW_FRAME_RATE_NO_VOTE: return FrameRateCompatibility::NoVote; diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 767000e6b2..f9bfe97dcb 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -584,7 +584,7 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_vrr) { auto layer = createLegacyAndFrontedEndLayer(1); showLayer(1); - setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE, + setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); setFrameRateCategory(1, 0); @@ -623,7 +623,7 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_nonVrr) { auto layer = createLegacyAndFrontedEndLayer(1); showLayer(1); - setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE, + setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); setFrameRateCategory(1, 0); -- cgit v1.2.3-59-g8ed1b From e3a50455cb506c19251e5f4245b09127caed4b7a Mon Sep 17 00:00:00 2001 From: Daniele Di Proietto Date: Fri, 27 Sep 2024 14:43:39 +0000 Subject: tracing_perfetto: Use 1Mb shared memory buffer The default shared memory buffer with perfetto is 256 Kb. With the current chunking configuration this allows no more than 64 threads tracing at the same time. Using a 1Mb buffer allows 256 threads. Tested by recording a trace with: ``` buffers { size_kb: 100024 fill_policy: RING_BUFFER } data_sources { config { name: "linux.ftrace" ftrace_config { atrace_categories: "gfx" atrace_categories: "input" atrace_categories: "view" atrace_categories: "webview" atrace_categories: "wm" atrace_categories: "am" atrace_categories: "sm" atrace_categories: "ss" atrace_categories_prefer_sdk: "gfx" atrace_categories_prefer_sdk: "input" atrace_categories_prefer_sdk: "view" atrace_categories_prefer_sdk: "webview" atrace_categories_prefer_sdk: "wm" atrace_categories_prefer_sdk: "am" atrace_categories_prefer_sdk: "sm" atrace_categories_prefer_sdk: "ss" atrace_categories_prefer_sdk: "pdx" } } } data_sources { config { name: "track_event" track_event_config { enabled_categories: "gfx" enabled_categories: "input" enabled_categories: "view" enabled_categories: "webview" enabled_categories: "wm" enabled_categories: "am" enabled_categories: "sm" enabled_categories: "ss" disabled_categories: "*" } } } ``` and checking that the shared memory buffer used with system_server, systemui and surfaceflinger is 1Mb: ``` vsoc_x86_64:/ # pidof system_server 801 vsoc_x86_64:/ # pidof com.android.systemui 4355 vsoc_x86_64:/ # pidof /system/bin/surfaceflinger 629 vsoc_x86_64:/ # cat /proc/801/maps | grep perfetto_shmem 7e95d774a000-7e95d784a000 rw-s 00000000 00:01 5899 /memfd:perfetto_shmem (deleted) vsoc_x86_64:/ # cat /proc/4355/maps | grep perfetto_shmem 7e9708a54000-7e9708b54000 rw-s 00000000 00:01 5900 /memfd:perfetto_shmem (deleted) vsoc_x86_64:/ # cat /proc/629/maps | grep perfetto_shmem 7379e57e5000-7379e58e5000 rw-s 00000000 00:01 5901 /memfd:perfetto_shmem (deleted) ``` Flag: android.os.perfetto_sdk_tracing Bug: 303199244 Change-Id: Ia3ecf93f3387f0d6c0fe988142f7dd66c9d4a993 --- libs/tracing_perfetto/tracing_perfetto_internal.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'libs') diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index c4f866338a..a58bc77131 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -236,6 +236,7 @@ void registerWithPerfetto(bool test) { std::call_once(registration, [test]() { struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); args.backends = test ? PERFETTO_BACKEND_IN_PROCESS : PERFETTO_BACKEND_SYSTEM; + args.shmem_size_hint_kb = 1024; PerfettoProducerInit(args); PerfettoTeInit(); PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES); -- cgit v1.2.3-59-g8ed1b From 917d6da6170b187ef05f3be757ad7ad822802068 Mon Sep 17 00:00:00 2001 From: Harry Cutts Date: Mon, 2 Dec 2024 18:33:09 +0000 Subject: libgui_tests: demote "Linked to input" log to info This log indicates the expected, correct behaviour, so it shouldn't be logged as an error. Bug: 379758401 Change-Id: If347466408c6836251666246e7dc3d5ffc914782 Test: Treehugger Flag: TEST_ONLY --- libs/gui/tests/EndToEndNativeInputTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 0e84d68eec..dcda1eedd0 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -75,7 +75,7 @@ sp getInputFlinger() { if (input == nullptr) { ALOGE("Failed to link to input service"); } else { - ALOGE("Linked to input"); + ALOGI("Linked to input"); } return interface_cast(input); } -- cgit v1.2.3-59-g8ed1b From a2ab5be9799866b2c6d10285adea4e09ffd49657 Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Wed, 4 Dec 2024 18:30:35 +0000 Subject: tests: Improve logging for objects that are causing errors In b/374973152, we're seeing error logs surrounding a generic "Virtual disp consumer", which is used in several places. This makes it harder to understand what's broken. This change clarifies the names. Bug: 374973152 Flag: EXEMPT tests Test: atest Change-Id: Ib1974f304f6c64c1d7ae02e0b77ec829d14ba0f1 --- libs/gui/tests/EndToEndNativeInputTest.cpp | 2 +- services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp | 2 +- services/surfaceflinger/tests/TransactionTestHarnesses.h | 4 ++-- services/surfaceflinger/tests/VirtualDisplay_test.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 0e84d68eec..a1c7cbf416 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -1212,7 +1212,7 @@ public: sp consumer; sp producer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->setConsumerName(String8("Virtual disp consumer")); + consumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayTests)")); consumer->setDefaultBufferSize(width, height); mProducers.push_back(producer); diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp index 56cf13d7fe..350e347ae3 100644 --- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp +++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp @@ -47,7 +47,7 @@ protected: sp consumer; BufferQueue::createBufferQueue(&mProducer, &consumer); - consumer->setConsumerName(String8("Virtual disp consumer")); + consumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayLayerBounds)")); consumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(), mMainDisplayMode.resolution.getHeight()); } diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index bf5957a89a..c95c875746 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -58,7 +58,7 @@ public: GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_SW_READ_OFTEN); sp listener = sp::make(this); itemConsumer->setFrameAvailableListener(listener); - itemConsumer->setName(String8("Virtual disp consumer")); + itemConsumer->setName(String8("Virtual disp consumer (TransactionTest)")); itemConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight()); #else sp producer; @@ -66,7 +66,7 @@ public: sp itemConsumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->setConsumerName(String8("Virtual disp consumer")); + consumer->setConsumerName(String8("Virtual disp consumer (TransactionTest)")); consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight()); itemConsumer = sp::make(consumer, diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp index d69378cec2..1108c7fe56 100644 --- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp +++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp @@ -29,14 +29,14 @@ protected: void SetUp() override { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) mGLConsumer = sp::make(GLConsumer::TEXTURE_EXTERNAL, true, false, false); - mGLConsumer->setName(String8("Virtual disp consumer")); + mGLConsumer->setName(String8("Virtual disp consumer (VirtualDisplayTest)")); mGLConsumer->setDefaultBufferSize(100, 100); mProducer = mGLConsumer->getSurface()->getIGraphicBufferProducer(); #else sp consumer; BufferQueue::createBufferQueue(&mProducer, &consumer); - consumer->setConsumerName(String8("Virtual disp consumer")); + consumer->setConsumerName(String8("Virtual disp consumer (VirtualDisplayTest)")); consumer->setDefaultBufferSize(100, 100); mGLConsumer = sp::make(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false); -- cgit v1.2.3-59-g8ed1b From 87468c3c28ce90548758c7aaf34efb5013bd3287 Mon Sep 17 00:00:00 2001 From: Ram Indani Date: Wed, 4 Dec 2024 21:00:22 +0000 Subject: Update invalid Compatibility to 3 ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE uses the value 2 for the compatibility, and will fail the check for invalid compatibility. Increasing the compatibility to 3 for invalid test case BUG: 382247134 Test: atest FrameRateUtilsTest Flag: EXEMPT test fix Change-Id: If3ceac2f673221244a16fd499b80e7929ecb50f6 --- libs/gui/tests/FrameRateUtilsTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp index 5d3287d373..9ffe91f460 100644 --- a/libs/gui/tests/FrameRateUtilsTest.cpp +++ b/libs/gui/tests/FrameRateUtilsTest.cpp @@ -62,7 +62,7 @@ TEST(FrameRateUtilsTest, ValidateFrameRate) { // Invalid compatibility. EXPECT_FALSE( ValidateFrameRate(60.0f, -1, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); - EXPECT_FALSE(ValidateFrameRate(60.0f, 2, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_FALSE(ValidateFrameRate(60.0f, 3, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); // Invalid change frame rate strategy. if (flags::bq_setframerate()) { -- cgit v1.2.3-59-g8ed1b From f48bef8ed6b6733bb75591fc919620a0fdbf336f Mon Sep 17 00:00:00 2001 From: Jim Shargo Date: Wed, 4 Dec 2024 18:56:59 +0000 Subject: tests: Eliminate annoying and noisy CompositionEngine errors These tests create surfaces for virtual displays, but delete the consumer side of the BQ before they attach, causing errors in SF that spam logs and seem confusing. In this change, we keep the consumers around for the lifetime of the virtual displays. Bug: 374973152 Flag: EXEMPT tests Test: atest BYPASS_IGBP_IGBC_API_REASON=fixing old tests Change-Id: I70943093652516b8b083a8c46c82d9ddbea56695 --- libs/gui/tests/EndToEndNativeInputTest.cpp | 14 ++++++++++++-- .../tests/MultiDisplayLayerBounds_test.cpp | 18 +++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index a1c7cbf416..c77a30a5ff 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -1214,7 +1216,15 @@ public: BufferQueue::createBufferQueue(&producer, &consumer); consumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayTests)")); consumer->setDefaultBufferSize(width, height); - mProducers.push_back(producer); + + class StubConsumerListener : public BnConsumerListener { + virtual void onFrameAvailable(const BufferItem&) override {} + virtual void onBuffersReleased() override {} + virtual void onSidebandStreamChanged() override {} + }; + + consumer->consumerConnect(sp::make(), true); + mBufferQueues.push_back({consumer, producer}); std::string name = "VirtualDisplay"; name += std::to_string(mVirtualDisplays.size()); @@ -1231,7 +1241,7 @@ public: } std::vector> mVirtualDisplays; - std::vector> mProducers; + std::vector, sp>> mBufferQueues; }; TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) { diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp index 350e347ae3..65add63165 100644 --- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp +++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp @@ -19,6 +19,7 @@ #pragma clang diagnostic ignored "-Wconversion" #include +#include #include #include "LayerTransactionTest.h" @@ -45,11 +46,17 @@ protected: SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState); SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode); - sp consumer; - BufferQueue::createBufferQueue(&mProducer, &consumer); - consumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayLayerBounds)")); - consumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(), - mMainDisplayMode.resolution.getHeight()); + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + mConsumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayLayerBounds)")); + mConsumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(), + mMainDisplayMode.resolution.getHeight()); + + class StubConsumerListener : public BnConsumerListener { + virtual void onFrameAvailable(const BufferItem&) override {} + virtual void onBuffersReleased() override {} + virtual void onSidebandStreamChanged() override {} + }; + mConsumer->consumerConnect(sp::make(), true); } virtual void TearDown() { @@ -92,6 +99,7 @@ protected: sp mMainDisplay; PhysicalDisplayId mMainDisplayId; sp mVirtualDisplay; + sp mConsumer; sp mProducer; sp mColorLayer; Color mExpectedColor = {63, 63, 195, 255}; -- cgit v1.2.3-59-g8ed1b From a9ec7d4f1e80b0d0bacb448bb37dbc8e047c3386 Mon Sep 17 00:00:00 2001 From: Arpit Singh Date: Wed, 4 Dec 2024 14:34:05 +0000 Subject: Use unique names for InputSurface in e2eNativeInputTest Some tests create multiple input surfaces using name default name which makes it harder to understand debug logs. This CL specifies unique explicit names for surfaces created in such tests. Test: atest InputSurfacesTest Bug: 379758401 Flag: EXEMPT TEST_ONLY Change-Id: I9ad5fc8b343304c1096d9c658b8124f506695c50 --- libs/gui/tests/EndToEndNativeInputTest.cpp | 135 ++++++++++++++++------------- 1 file changed, 74 insertions(+), 61 deletions(-) (limited to 'libs') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index dcda1eedd0..45f41f1e8f 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -116,7 +116,7 @@ public: } else { android::os::InputChannelCore tempChannel; android::binder::Status result = - mInputFlinger->createInputChannel("testchannels", &tempChannel); + mInputFlinger->createInputChannel(sc->getName() + " channel", &tempChannel); if (!result.isOk()) { ADD_FAILURE() << "binder call to createInputChannel failed"; } @@ -139,47 +139,51 @@ public: } static std::unique_ptr makeColorInputSurface(const sp& scc, - int width, int height) { + int width, int height, + const std::string& name) { sp surfaceControl = - scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */, + scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect); return std::make_unique(surfaceControl, width, height); } static std::unique_ptr makeBufferInputSurface( - const sp& scc, int width, int height) { + const sp& scc, int width, int height, + const std::string& name = "Test Buffer Surface") { sp surfaceControl = - scc->createSurface(String8("Test Buffer Surface"), width, height, - PIXEL_FORMAT_RGBA_8888, 0 /* flags */); + scc->createSurface(String8(name.c_str()), width, height, PIXEL_FORMAT_RGBA_8888, + 0 /* flags */); return std::make_unique(surfaceControl, width, height); } static std::unique_ptr makeContainerInputSurface( - const sp& scc, int width, int height) { + const sp& scc, int width, int height, + const std::string& name = "Test Container Surface") { sp surfaceControl = - scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */, - 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888, + scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceContainer); return std::make_unique(surfaceControl, width, height); } static std::unique_ptr makeContainerInputSurfaceNoInputChannel( - const sp& scc, int width, int height) { + const sp& scc, int width, int height, + const std::string& name = "Test Container Surface") { sp surfaceControl = - scc->createSurface(String8("Test Container Surface"), 100 /* height */, - 100 /* width */, PIXEL_FORMAT_RGBA_8888, + scc->createSurface(String8(name.c_str()), 100 /* height */, 100 /* width */, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceContainer); return std::make_unique(surfaceControl, width, height, true /* noInputChannel */); } static std::unique_ptr makeCursorInputSurface( - const sp& scc, int width, int height) { + const sp& scc, int width, int height, + const std::string& name = "Test Cursor Surface") { sp surfaceControl = - scc->createSurface(String8("Test Cursor Surface"), 0 /* bufHeight */, - 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eCursorWindow); + scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eCursorWindow); return std::make_unique(surfaceControl, width, height); } @@ -410,8 +414,9 @@ public: void TearDown() { mComposerClient->dispose(); } - std::unique_ptr makeSurface(int width, int height) { - return InputSurface::makeColorInputSurface(mComposerClient, width, height); + std::unique_ptr makeSurface(int width, int height, + const std::string& name = "Test Surface") const { + return InputSurface::makeColorInputSurface(mComposerClient, width, height, name); } void postBuffer(const sp& layer, int32_t w, int32_t h) { @@ -470,10 +475,10 @@ TEST_F(InputSurfacesTest, can_receive_input) { * reverse order. */ TEST_F(InputSurfacesTest, input_respects_positioning) { - std::unique_ptr surface = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Left Surface"); surface->showAt(100, 100); - std::unique_ptr surface2 = makeSurface(100, 100); + std::unique_ptr surface2 = makeSurface(100, 100, "Right Surface"); surface2->showAt(200, 200); injectTap(201, 201); @@ -493,8 +498,8 @@ TEST_F(InputSurfacesTest, input_respects_positioning) { } TEST_F(InputSurfacesTest, input_respects_layering) { - std::unique_ptr surface = makeSurface(100, 100); - std::unique_ptr surface2 = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Test Surface 1"); + std::unique_ptr surface2 = makeSurface(100, 100, "Test Surface 2"); surface->showAt(10, 10); surface2->showAt(10, 10); @@ -519,8 +524,8 @@ TEST_F(InputSurfacesTest, input_respects_layering) { // (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start // of the client content. TEST_F(InputSurfacesTest, input_respects_surface_insets) { - std::unique_ptr bgSurface = makeSurface(100, 100); - std::unique_ptr fgSurface = makeSurface(100, 100); + std::unique_ptr bgSurface = makeSurface(100, 100, "Background Surface"); + std::unique_ptr fgSurface = makeSurface(100, 100, "Foreground Surface"); bgSurface->showAt(100, 100); fgSurface->mInputInfo->editInfo()->surfaceInset = 5; @@ -534,8 +539,8 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { } TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableRegionWithCrop) { - std::unique_ptr bgSurface = makeSurface(100, 100); - std::unique_ptr fgSurface = makeSurface(100, 100); + std::unique_ptr bgSurface = makeSurface(100, 100, "Background Surface"); + std::unique_ptr fgSurface = makeSurface(100, 100, "Foreground Surface"); bgSurface->showAt(100, 100); fgSurface->mInputInfo->editInfo()->surfaceInset = 5; @@ -551,8 +556,8 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableReg // Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463 TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { - std::unique_ptr parentSurface = makeSurface(100, 100); - std::unique_ptr childSurface = makeSurface(100, 100); + std::unique_ptr parentSurface = makeSurface(100, 100, "Parent Surface"); + std::unique_ptr childSurface = makeSurface(100, 100, "Child Surface"); parentSurface->showAt(100, 100); childSurface->mInputInfo->editInfo()->surfaceInset = 10; @@ -572,8 +577,8 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { // Ensure a surface whose insets are scaled, handles the touch offset correctly. TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { - std::unique_ptr bgSurface = makeSurface(100, 100); - std::unique_ptr fgSurface = makeSurface(100, 100); + std::unique_ptr bgSurface = makeSurface(100, 100, "Background Surface"); + std::unique_ptr fgSurface = makeSurface(100, 100, "Foreground Surface"); bgSurface->showAt(100, 100); fgSurface->mInputInfo->editInfo()->surfaceInset = 5; @@ -590,8 +595,8 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { } TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { - std::unique_ptr bgSurface = makeSurface(100, 100); - std::unique_ptr fgSurface = makeSurface(100, 100); + std::unique_ptr bgSurface = makeSurface(100, 100, "Background Surface"); + std::unique_ptr fgSurface = makeSurface(100, 100, "Foreground Surface"); bgSurface->showAt(100, 100); // In case we pass the very big inset without any checking. @@ -621,8 +626,8 @@ TEST_F(InputSurfacesTest, touchable_region) { } TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) { - std::unique_ptr bgSurface = makeSurface(100, 100); - std::unique_ptr fgSurface = makeSurface(100, 100); + std::unique_ptr bgSurface = makeSurface(100, 100, "Background Surface"); + std::unique_ptr fgSurface = makeSurface(100, 100, "Foreground Surface"); bgSurface->showAt(100, 100); // Set the touchable region to the values at the limit of its corresponding type. @@ -641,8 +646,8 @@ TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) { } TEST_F(InputSurfacesTest, input_respects_scaled_touchable_region_overflow) { - std::unique_ptr bgSurface = makeSurface(100, 100); - std::unique_ptr fgSurface = makeSurface(100, 100); + std::unique_ptr bgSurface = makeSurface(100, 100, "Background Surface"); + std::unique_ptr fgSurface = makeSurface(100, 100, "Foreground Surface"); bgSurface->showAt(0, 0); fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf( @@ -707,8 +712,8 @@ TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) { } TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { - std::unique_ptr bgSurface = makeSurface(100, 100); - std::unique_ptr fgSurface = makeSurface(100, 100); + std::unique_ptr bgSurface = makeSurface(100, 100, "Background Surface"); + std::unique_ptr fgSurface = makeSurface(100, 100, "Foreground Surface"); bgSurface->showAt(10, 10); fgSurface->showAt(10, 10); @@ -839,12 +844,13 @@ TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) { } TEST_F(InputSurfacesTest, touch_flag_obscured) { - std::unique_ptr surface = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Touchable Surface"); surface->showAt(100, 100); // Add non touchable window to fully cover touchable window. Window behind gets touch, but // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED - std::unique_ptr nonTouchableSurface = makeSurface(100, 100); + std::unique_ptr nonTouchableSurface = + makeSurface(100, 100, "Non-Touchable Surface"); nonTouchableSurface->mInputInfo->editInfo() ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; @@ -858,14 +864,15 @@ TEST_F(InputSurfacesTest, touch_flag_obscured) { } TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) { - std::unique_ptr surface = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Test Surface"); surface->showAt(100, 100); // Add non touchable window to cover touchable window, but parent is cropped to not cover area // that will be tapped. Window behind gets touch, but with flag // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED - std::unique_ptr parentSurface = makeSurface(100, 100); - std::unique_ptr nonTouchableSurface = makeSurface(100, 100); + std::unique_ptr parentSurface = makeSurface(100, 100, "Parent Surface"); + std::unique_ptr nonTouchableSurface = + makeSurface(100, 100, "Non-Touchable Surface"); nonTouchableSurface->mInputInfo->editInfo() ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, @@ -885,13 +892,14 @@ TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) { } TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) { - std::unique_ptr surface = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Test Surface"); surface->showAt(100, 100); // Add non touchable window to cover touchable window, but parent is cropped to avoid covering // the touchable window. Window behind gets touch with no obscured flags. - std::unique_ptr parentSurface = makeSurface(100, 100); - std::unique_ptr nonTouchableSurface = makeSurface(100, 100); + std::unique_ptr parentSurface = makeSurface(100, 100, "Parent Surface"); + std::unique_ptr nonTouchableSurface = + makeSurface(100, 100, "Non-Touchable Surface"); nonTouchableSurface->mInputInfo->editInfo() ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, @@ -975,12 +983,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) { } TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { - std::unique_ptr surface = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Test Surface"); surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111}; surface->doTransaction( [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); surface->showAt(100, 100); - std::unique_ptr obscuringSurface = makeSurface(100, 100); + std::unique_ptr obscuringSurface = makeSurface(100, 100, "Obscuring Surface"); obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; @@ -995,12 +1003,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { } TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { - std::unique_ptr surface = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Test Surface"); surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111}; surface->doTransaction( [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); surface->showAt(100, 100); - std::unique_ptr obscuringSurface = makeSurface(100, 100); + std::unique_ptr obscuringSurface = makeSurface(100, 100, "Obscuring Surface"); obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; @@ -1017,10 +1025,10 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { } TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { - std::unique_ptr parentSurface = makeSurface(300, 300); + std::unique_ptr parentSurface = makeSurface(300, 300, "Parent Surface"); parentSurface->showAt(0, 0, Rect(0, 0, 300, 300)); - std::unique_ptr surface = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Test Surface"); surface->showAt(100, 100); surface->doTransaction([&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); @@ -1039,10 +1047,10 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { } TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { - std::unique_ptr parentSurface = makeSurface(300, 300); + std::unique_ptr parentSurface = makeSurface(300, 300, "Parent Surface"); parentSurface->showAt(0, 0, Rect(0, 0, 300, 300)); - std::unique_ptr surface = makeSurface(100, 100); + std::unique_ptr surface = makeSurface(100, 100, "Test Surface"); surface->doTransaction([&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); t.reparent(sc, parentSurface->mSurfaceControl); @@ -1105,9 +1113,11 @@ TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) { */ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_crop) { std::unique_ptr parentContainer = - InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0, + "Parent Container Surface"); std::unique_ptr containerSurface = - InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100, + "Test Container Surface"); containerSurface->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; @@ -1130,11 +1140,14 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ */ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) { std::unique_ptr bgContainer = - InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0, + "Background Container Surface"); std::unique_ptr parentContainer = - InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0, + "Parent Container Surface"); std::unique_ptr containerSurface = - InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100, + "Test Container Surface"); containerSurface->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; @@ -1160,11 +1173,11 @@ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_nul */ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { std::unique_ptr cropLayer = - InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0, "Crop Layer Surface"); cropLayer->showAt(50, 50, Rect(0, 0, 20, 20)); std::unique_ptr containerSurface = - InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100, "Container Surface"); containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = cropLayer->mSurfaceControl->getHandle(); -- cgit v1.2.3-59-g8ed1b From ffda356e76111861c0e44ac4e966fe576fc5f976 Mon Sep 17 00:00:00 2001 From: Dheepthi S Date: Fri, 6 Dec 2024 21:52:24 +0000 Subject: Add AHARDWAREBUFFER_FORMAT_YCBCR_P210... * To PublicFormat * To various checks in AHardwareBuffer Bug: 370458610 Flag: EXEMPT NDK Change-Id: I7e82febd419bc9cd6cbd74ff6fef168d864eb784 --- libs/nativewindow/AHardwareBuffer.cpp | 9 ++++++++- libs/ui/include/ui/PublicFormat.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index ca41346d46..3205c32f01 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -112,6 +112,10 @@ static_assert( static_cast(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) == AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM, "HAL and AHardwareBuffer pixel format don't match"); +static_assert( + static_cast(aidl::android::hardware::graphics::common::PixelFormat::YCBCR_P210) == + AHARDWAREBUFFER_FORMAT_YCbCr_P210, + "HAL and AHardwareBuffer pixel format don't match"); static enum AHardwareBufferStatus filterStatus(status_t status) { switch (status) { @@ -300,8 +304,10 @@ int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, if (result == 0) { outPlanes->planeCount = 3; outPlanes->planes[0].data = yuvData.y; - // P010 is word-aligned 10-bit semiplaner, and YCbCr_422_I is a single interleaved plane + // P010 & P210 are word-aligned 10-bit semiplaner, and YCbCr_422_I is a single interleaved + // plane if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010 || + format == AHARDWAREBUFFER_FORMAT_YCbCr_P210 || format == AHARDWAREBUFFER_FORMAT_YCbCr_422_I) { outPlanes->planes[0].pixelStride = 2; } else { @@ -724,6 +730,7 @@ bool AHardwareBuffer_formatIsYuv(uint32_t format) { case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP: case AHARDWAREBUFFER_FORMAT_YCbCr_422_I: case AHARDWAREBUFFER_FORMAT_YCbCr_P010: + case AHARDWAREBUFFER_FORMAT_YCbCr_P210: return true; default: return false; diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h index e87931efed..7c17763860 100644 --- a/libs/ui/include/ui/PublicFormat.h +++ b/libs/ui/include/ui/PublicFormat.h @@ -60,6 +60,7 @@ enum class PublicFormat { JPEG_R = 0x1005, HEIC = 0x48454946, HEIC_ULTRAHDR = 0x1006, + YCBCR_P210 = 0x3c, }; /* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL -- cgit v1.2.3-59-g8ed1b