diff options
20 files changed, 1399 insertions, 99 deletions
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp index 0183621299..fedc7893db 100644 --- a/opengl/libs/EGL/getProcAddress.cpp +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -80,46 +80,44 @@ namespace android { #elif defined(__i386__) - #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api + #define API_ENTRY(_api) __attribute__((naked)) _api #define CALL_GL_EXTENSION_API(_api) \ - register void** fn; \ __asm__ volatile( \ - "mov %%gs:0, %[fn]\n" \ - "mov %P[tls](%[fn]), %[fn]\n" \ - "test %[fn], %[fn]\n" \ - "cmovne %P[api](%[fn]), %[fn]\n" \ - "test %[fn], %[fn]\n" \ + "mov %%gs:0, %%eax\n" \ + "mov %P[tls](%%eax), %%eax\n" \ + "test %%eax, %%eax\n" \ + "cmovne %P[api](%%eax), %%eax\n" \ + "test %%eax, %%eax\n" \ "je 1f\n" \ - "jmp *%[fn]\n" \ - "1:\n" \ - : [fn] "=r" (fn) \ + "jmp *%%eax\n" \ + "1: ret\n" \ + : \ : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \ [api] "i" (__builtin_offsetof(gl_hooks_t, \ ext.extensions[_api])) \ - : "cc" \ + : "eax", "cc" \ ); #elif defined(__x86_64__) - #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api + #define API_ENTRY(_api) __attribute__((naked)) _api #define CALL_GL_EXTENSION_API(_api) \ - register void** fn; \ __asm__ volatile( \ - "mov %%fs:0, %[fn]\n" \ - "mov %P[tls](%[fn]), %[fn]\n" \ - "test %[fn], %[fn]\n" \ - "cmovne %P[api](%[fn]), %[fn]\n" \ - "test %[fn], %[fn]\n" \ + "mov %%fs:0, %%rax\n" \ + "mov %P[tls](%%rax), %%rax\n" \ + "test %%rax, %%rax\n" \ + "cmovne %P[api](%%rax), %%rax\n" \ + "test %%rax, %%rax\n" \ "je 1f\n" \ - "jmp *%[fn]\n" \ - "1:\n" \ - : [fn] "=r" (fn) \ + "jmp *%%rax\n" \ + "1: ret\n" \ + : \ : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \ [api] "i" (__builtin_offsetof(gl_hooks_t, \ ext.extensions[_api])) \ - : "cc" \ + : "rax", "cc" \ ); #elif defined(__mips64) diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 3947318262..61758b63f1 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -30,8 +30,9 @@ #include <android/configuration.h> -#include <algorithm> #include <inttypes.h> +#include <algorithm> +#include <iterator> #include <set> using android::Fence; @@ -338,6 +339,31 @@ Error Display::getActiveConfig( return Error::None; } +Error Display::getActiveConfigIndex(int* outIndex) const { + ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId); + hwc2_config_t configId = 0; + auto intError = mComposer.getActiveConfig(mId, &configId); + auto error = static_cast<Error>(intError); + + if (error != Error::None) { + ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId); + *outIndex = -1; + return error; + } + + auto pos = mConfigs.find(configId); + if (pos != mConfigs.end()) { + *outIndex = std::distance(mConfigs.begin(), pos); + } else { + ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId); + // Return no error, but the caller needs to check for a negative index + // to detect this case + *outIndex = -1; + } + + return Error::None; +} + Error Display::getChangedCompositionTypes( std::unordered_map<Layer*, Composition>* outTypes) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 3ac06ec192..29d7a47ad8 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -36,7 +36,6 @@ #include <unordered_map> #include <unordered_set> #include <vector> -#include <map> namespace android { class Fence; @@ -207,6 +206,7 @@ public: [[clang::warn_unused_result]] Error destroyLayer(Layer* layer); [[clang::warn_unused_result]] Error getActiveConfig( std::shared_ptr<const Config>* outConfig) const; + [[clang::warn_unused_result]] Error getActiveConfigIndex(int* outIndex) const; [[clang::warn_unused_result]] Error getChangedCompositionTypes( std::unordered_map<Layer*, Composition>* outTypes); [[clang::warn_unused_result]] Error getColorModes( @@ -288,9 +288,7 @@ private: bool mIsConnected; DisplayType mType; std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers; - // The ordering in this map matters, for getConfigs(), when it is - // converted to a vector - std::map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs; + std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs; }; // Convenience C++ class to access hwc2_device_t Layer functions directly. diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 96d691ce9c..f5f7a821f0 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -318,6 +318,28 @@ std::shared_ptr<const HWC2::Display::Config> return config; } +int HWComposer::getActiveConfigIndex(int32_t displayId) const { + if (!isValidDisplay(displayId)) { + ALOGV("getActiveConfigIndex: Attempted to access invalid display %d", displayId); + return -1; + } + int index; + auto error = mDisplayData[displayId].hwcDisplay->getActiveConfigIndex(&index); + if (error == HWC2::Error::BadConfig) { + ALOGE("getActiveConfigIndex: No config active, returning -1"); + return -1; + } else if (error != HWC2::Error::None) { + ALOGE("getActiveConfigIndex failed for display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return -1; + } else if (index < 0) { + ALOGE("getActiveConfigIndex returned an unknown config for display %d", displayId); + return -1; + } + + return index; +} + std::vector<ui::ColorMode> HWComposer::getColorModes(int32_t displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, {}); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index d7f3b08d48..f9689482cc 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -164,6 +164,7 @@ public: std::shared_ptr<const HWC2::Display::Config> getActiveConfig(int32_t displayId) const; + int getActiveConfigIndex(int32_t displayId) const; std::vector<ui::ColorMode> getColorModes(int32_t displayId) const; diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/EventControlThread.h index 9be4e7cd8e..cafae53400 100644 --- a/services/surfaceflinger/EventControlThread.h +++ b/services/surfaceflinger/EventControlThread.h @@ -26,8 +26,6 @@ namespace android { -class SurfaceFlinger; - class EventControlThread { public: virtual ~EventControlThread(); diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp index bb9c0703ed..bc271c8ec5 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/EventThread.cpp @@ -26,14 +26,12 @@ #include <cutils/sched_policy.h> #include <gui/DisplayEventReceiver.h> -#include <gui/IDisplayEventConnection.h> #include <utils/Errors.h> #include <utils/String8.h> #include <utils/Trace.h> #include "EventThread.h" -#include "SurfaceFlinger.h" using namespace std::chrono_literals; @@ -47,9 +45,11 @@ EventThread::~EventThread() = default; namespace impl { -EventThread::EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs, - const char* threadName) - : mVSyncSource(src), mFlinger(flinger), mInterceptVSyncs(interceptVSyncs) { +EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) + : mVSyncSource(src), + mResyncWithRateLimitCallback(resyncWithRateLimitCallback), + mInterceptVSyncsCallback(interceptVSyncsCallback) { for (auto& event : mVSyncEvent) { event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; event.header.id = 0; @@ -118,7 +118,9 @@ void EventThread::setVsyncRate(uint32_t count, const sp<EventThread::Connection> void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) { std::lock_guard<std::mutex> lock(mMutex); - mFlinger.resyncWithRateLimit(); + if (mResyncWithRateLimitCallback) { + mResyncWithRateLimitCallback(); + } if (connection->count < 0) { connection->count = 0; @@ -216,8 +218,8 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( timestamp = mVSyncEvent[i].header.timestamp; if (timestamp) { // we have a vsync event to dispatch - if (mInterceptVSyncs) { - mFlinger.mInterceptor->saveVSyncEvent(timestamp); + if (mInterceptVSyncsCallback) { + mInterceptVSyncsCallback(timestamp); } *event = mVSyncEvent[i]; mVSyncEvent[i].header.timestamp = 0; diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h index 97f0a359e2..9c13ed2755 100644 --- a/services/surfaceflinger/EventThread.h +++ b/services/surfaceflinger/EventThread.h @@ -37,6 +37,7 @@ namespace android { // --------------------------------------------------------------------------- +class EventThreadTest; class SurfaceFlinger; class String8; @@ -82,7 +83,9 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { class Connection : public BnDisplayEventConnection { public: explicit Connection(EventThread* eventThread); - status_t postEvent(const DisplayEventReceiver::Event& event); + virtual ~Connection(); + + virtual status_t postEvent(const DisplayEventReceiver::Event& event); // count >= 1 : continuous event. count is the vsync rate // count == 0 : one-shot event that has not fired @@ -90,7 +93,6 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { int32_t count; private: - virtual ~Connection(); virtual void onFirstRef(); status_t stealReceiveChannel(gui::BitTube* outChannel) override; status_t setVsyncRate(uint32_t count) override; @@ -100,8 +102,11 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { }; public: - EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs, - const char* threadName); + using ResyncWithRateLimitCallback = std::function<void()>; + using InterceptVSyncsCallback = std::function<void(nsecs_t)>; + + EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); ~EventThread(); sp<BnDisplayEventConnection> createEventConnection() const override; @@ -124,6 +129,8 @@ public: void setPhaseOffset(nsecs_t phaseOffset) override; private: + friend EventThreadTest; + void threadMain(); Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* event) @@ -137,8 +144,9 @@ private: void onVSyncEvent(nsecs_t timestamp) override; // constants - VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr; - SurfaceFlinger& mFlinger; + VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr; + const ResyncWithRateLimitCallback mResyncWithRateLimitCallback; + const InterceptVSyncsCallback mInterceptVSyncsCallback; std::thread mThread; mutable std::mutex mMutex; @@ -155,8 +163,6 @@ private: // for debugging bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false; - - const bool mInterceptVSyncs = false; }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 9043234132..2077598371 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1337,10 +1337,6 @@ bool Layer::setDataSpace(ui::Dataspace dataSpace) { return true; } -ui::Dataspace Layer::getDataSpace() const { - return mCurrentState.dataSpace; -} - uint32_t Layer::getLayerStack() const { auto p = mDrawingParent.promote(); if (p == nullptr) { @@ -1433,7 +1429,7 @@ LayerDebugInfo Layer::getLayerDebugInfo() const { info.mColor = ds.color; info.mFlags = ds.flags; info.mPixelFormat = getPixelFormat(); - info.mDataSpace = static_cast<android_dataspace>(getDataSpace()); + info.mDataSpace = static_cast<android_dataspace>(ds.dataSpace); info.mMatrix[0][0] = ds.active.transform[0][0]; info.mMatrix[0][1] = ds.active.transform[0][1]; info.mMatrix[1][0] = ds.active.transform[1][0]; @@ -1960,7 +1956,7 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) layerInfo->set_is_opaque(isOpaque(state)); layerInfo->set_invalidate(contentDirty); - layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace()))); + layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(state.dataSpace))); layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat())); LayerProtoHelper::writeToProto(getColor(), layerInfo->mutable_color()); LayerProtoHelper::writeToProto(state.color, layerInfo->mutable_requested_color()); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 632efbe23e..91eb15ae4a 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -288,7 +288,6 @@ public: bool setFlags(uint8_t flags, uint8_t mask); bool setLayerStack(uint32_t layerStack); bool setDataSpace(ui::Dataspace dataSpace); - ui::Dataspace getDataSpace() const; uint32_t getLayerStack() const; void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber); void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber); diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp index aca6c7b805..90404fae6b 100644 --- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp +++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp @@ -147,6 +147,7 @@ GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags) mSrgbToXyz = srgb.getRGBtoXYZ(); mDisplayP3ToXyz = displayP3.getRGBtoXYZ(); mBt2020ToXyz = bt2020.getRGBtoXYZ(); + mXyzToSrgb = mat4(srgb.getXYZtoRGB()); mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB()); mXyzToBt2020 = mat4(bt2020.getXYZtoRGB()); } @@ -347,14 +348,17 @@ void GLES20RenderEngine::drawMesh(const Mesh& mesh) { break; } - // The supported output color spaces are Display P3 and BT2020. + // The supported output color spaces are BT2020, Display P3 and standard RGB. switch (outputStandard) { case Dataspace::STANDARD_BT2020: wideColorState.setOutputTransformMatrix(mXyzToBt2020); break; - default: + case Dataspace::STANDARD_DCI_P3: wideColorState.setOutputTransformMatrix(mXyzToDisplayP3); break; + default: + wideColorState.setOutputTransformMatrix(mXyzToSrgb); + break; } } else if (inputStandard != outputStandard) { // At this point, the input data space and output data space could be both diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h index 84a48136b0..de5761b9b0 100644 --- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h @@ -103,6 +103,7 @@ protected: mat3 mSrgbToXyz; mat3 mBt2020ToXyz; mat3 mDisplayP3ToXyz; + mat4 mXyzToSrgb; mat4 mXyzToDisplayP3; mat4 mXyzToBt2020; diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp index 7c2cb5b13c..2808a1a912 100644 --- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp +++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp @@ -422,13 +422,13 @@ void ProgramCache::generateOETF(Formatter& fs, const Key& needs) { case Key::OUTPUT_TF_ST2084: fs << R"__SHADER__( vec3 OETF(const vec3 linear) { - const float m1 = (2610.0 / 4096.0) / 4.0; - const float m2 = (2523.0 / 4096.0) * 128.0; - const float c1 = (3424.0 / 4096.0); - const float c2 = (2413.0 / 4096.0) * 32.0; - const float c3 = (2392.0 / 4096.0) * 32.0; + const highp float m1 = (2610.0 / 4096.0) / 4.0; + const highp float m2 = (2523.0 / 4096.0) * 128.0; + const highp float c1 = (3424.0 / 4096.0); + const highp float c2 = (2413.0 / 4096.0) * 32.0; + const highp float c3 = (2392.0 / 4096.0) * 32.0; - vec3 tmp = pow(linear, vec3(m1)); + highp vec3 tmp = pow(linear, vec3(m1)); tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); return pow(tmp, vec3(m2)); } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 84893a7c2e..9117207544 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -635,14 +635,21 @@ void SurfaceFlinger::init() { mEventThreadSource = std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs, true, "app"); - mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(), *this, false, + mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(), + [this]() { resyncWithRateLimit(); }, + impl::EventThread::InterceptVSyncsCallback(), "appEventThread"); mSfEventThreadSource = std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf"); - mSFEventThread = std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), *this, true, - "sfEventThread"); + mSFEventThread = + std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), + [this]() { resyncWithRateLimit(); }, + [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }, + "sfEventThread"); mEventQueue->setEventThread(mSFEventThread.get()); mVsyncModulator.setEventThread(mSFEventThread.get()); @@ -1131,9 +1138,11 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { ALOGV("VSync Injections enabled"); if (mVSyncInjector.get() == nullptr) { mVSyncInjector = std::make_unique<InjectVSyncSource>(); - mInjectorEventThread = - std::make_unique<impl::EventThread>(mVSyncInjector.get(), *this, false, - "injEventThread"); + mInjectorEventThread = std::make_unique< + impl::EventThread>(mVSyncInjector.get(), + [this]() { resyncWithRateLimit(); }, + impl::EventThread::InterceptVSyncsCallback(), + "injEventThread"); } mEventQueue->setEventThread(mInjectorEventThread.get()); } else { @@ -1870,7 +1879,7 @@ Dataspace SurfaceFlinger::getBestDataspace( *outHdrDataSpace = Dataspace::UNKNOWN; for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - switch (layer->getDataSpace()) { + switch (layer->getDrawingState().dataSpace) { case Dataspace::V0_SCRGB: case Dataspace::V0_SCRGB_LINEAR: bestDataSpace = Dataspace::V0_SCRGB_LINEAR; @@ -2089,13 +2098,13 @@ void SurfaceFlinger::setUpHWComposer() { "display %zd: %d", displayId, result); } for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - if ((layer->getDataSpace() == Dataspace::BT2020_PQ || - layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) && + if ((layer->getDrawingState().dataSpace == Dataspace::BT2020_PQ || + layer->getDrawingState().dataSpace == Dataspace::BT2020_ITU_PQ) && !displayDevice->hasHDR10Support()) { layer->forceClientComposition(hwcId); } - if ((layer->getDataSpace() == Dataspace::BT2020_HLG || - layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) && + if ((layer->getDrawingState().dataSpace == Dataspace::BT2020_HLG || + layer->getDrawingState().dataSpace == Dataspace::BT2020_ITU_HLG) && !displayDevice->hasHLGSupport()) { layer->forceClientComposition(hwcId); } @@ -2405,6 +2414,9 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( } setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace, RenderIntent::COLORIMETRIC); + if (state.type < DisplayDevice::DISPLAY_VIRTUAL) { + hw->setActiveConfig(getHwComposer().getActiveConfigIndex(state.type)); + } hw->setLayerStack(state.layerStack); hw->setProjection(state.orientation, state.viewport, state.frame); hw->setDisplayName(state.displayName); diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index bcabe0dffc..39761dd9ad 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -20,6 +20,8 @@ cc_test { srcs: [ ":libsurfaceflinger_sources", "DisplayTransactionTest.cpp", + "EventControlThreadTest.cpp", + "EventThreadTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplaySurface.cpp", "mock/gui/MockGraphicBufferConsumer.cpp", diff --git a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h new file mode 100644 index 0000000000..2245ee1a8a --- /dev/null +++ b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <chrono> +#include <deque> +#include <mutex> +#include <optional> +#include <thread> +#include <tuple> +#include <type_traits> +#include <utility> + +#include <android-base/thread_annotations.h> + +namespace android { + +// This class helps record calls made by another thread when they are made +// asynchronously, with no other way for the tests to verify that the calls have +// been made. +// +// A normal Google Mock recorder, while thread safe, does not allow you to wait +// for asynchronous calls to be made. +// +// Usage: +// +// In the test, use a Google Mock expectation to invoke an instance of the +// recorder: +// +// AsyncCallRecorder<void(int)> recorder; +// +// EXPECT_CALL(someMock, someFunction(_)). +// .WillRepeatedly(Invoke(recorder.getInvocable())); +// +// Then you can invoke the functionality being tested: +// +// threadUnderTest.doSomethingAsync() +// +// And afterwards make a number of assertions using the recorder: +// +// // Wait for one call (with reasonable default timeout), and get the args +// // as a std::tuple inside a std::optional. +// auto args = recorder.waitForCall(); +// // The returned std::optional will have a value if the recorder function +// // was called. +// ASSERT_TRUE(args.has_value()); +// // The arguments can be checked if needed using standard tuple +// // operations. +// EXPECT_EQ(123, std::get<0>(args.value())); +// +// Alternatively maybe you want to assert that a call was not made. +// +// EXPECT_FALSE(recorder.waitForUnexpectedCall().has_value()); +// +// However this check uses a really short timeout so as not to block the test +// unnecessarily. And it could be possible for the check to return false and +// then the recorder could observe a call being made after. +template <typename Func> +class AsyncCallRecorder; + +template <typename... Args> +class AsyncCallRecorder<void (*)(Args...)> { +public: + // For the tests, we expect the wait for an expected change to be signaled + // to be much shorter than this. + static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10}; + + // The wait here is tricky. We don't expect a change, but we don't want to + // wait forever (or for longer than the typical test function runtime). As + // even the simplest Google Test can take 1ms (1000us) to run, we wait for + // half that time. + static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500}; + + using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>; + + void recordCall(Args... args) { + std::lock_guard<std::mutex> lock(mMutex); + mCalls.emplace_back(std::make_tuple(args...)); + mCondition.notify_all(); + } + + // Returns a functor which can be used with the Google Mock Invoke() + // function, or as a std::function to record calls. + auto getInvocable() { + return [this](Args... args) { recordCall(args...); }; + } + + // Returns a set of arguments as a std::optional<std::tuple<...>> for the + // oldest call, waiting for the given timeout if necessary if there are no + // arguments in the FIFO. + std::optional<ArgTuple> waitForCall( + std::chrono::microseconds timeout = DEFAULT_CALL_EXPECTED_TIMEOUT) + NO_THREAD_SAFETY_ANALYSIS { + std::unique_lock<std::mutex> lock(mMutex); + + // Wait if necessary for us to have a record from a call. + mCondition.wait_for(lock, timeout, + [this]() NO_THREAD_SAFETY_ANALYSIS { return !mCalls.empty(); }); + + // Return the arguments from the oldest call, if one was made + bool called = !mCalls.empty(); + std::optional<ArgTuple> result; + if (called) { + result.emplace(std::move(mCalls.front())); + mCalls.pop_front(); + } + return result; + } + + // Waits using a small default timeout for when a call is not expected to be + // made. The returned std::optional<std:tuple<...>> should not have a value + // except if a set of arguments was unexpectedly received because a call was + // actually made. + // + // Note this function uses a small timeout to not block test execution, and + // it is possible the code under test could make the call AFTER the timeout + // expires. + std::optional<ArgTuple> waitForUnexpectedCall() { return waitForCall(UNEXPECTED_CALL_TIMEOUT); } + +private: + std::mutex mMutex; + std::condition_variable mCondition; + std::deque<ArgTuple> mCalls GUARDED_BY(mMutex); +}; + +// Like AsyncCallRecorder, but for when the function being invoked +// asynchronously is expected to return a value. +// +// This helper allows a single constant return value to be set to be returned by +// all calls that were made. +template <typename Func> +class AsyncCallRecorderWithCannedReturn; + +template <typename Ret, typename... Args> +class AsyncCallRecorderWithCannedReturn<Ret (*)(Args...)> + : public AsyncCallRecorder<void (*)(Args...)> { +public: + explicit AsyncCallRecorderWithCannedReturn(Ret returnvalue) : mReturnValue(returnvalue) {} + + auto getInvocable() { + return [this](Args... args) { + this->recordCall(args...); + return mReturnValue; + }; + } + +private: + const Ret mReturnValue; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 08da1a2c17..cd691282d0 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -45,12 +45,13 @@ using testing::Mock; using testing::Return; using testing::SetArgPointee; -using android::hardware::graphics::common::V1_0::Hdr; -using android::hardware::graphics::common::V1_1::ColorMode; -using android::hardware::graphics::common::V1_1::RenderIntent; +using android::Hwc2::ColorMode; using android::Hwc2::Error; +using android::Hwc2::Hdr; using android::Hwc2::IComposer; using android::Hwc2::IComposerClient; +using android::Hwc2::PerFrameMetadataKey; +using android::Hwc2::RenderIntent; using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; @@ -61,6 +62,8 @@ constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666; constexpr int32_t DEFAULT_DPI = 320; constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565; +constexpr int HWC_POWER_MODE_LEET = 1337; // An out of range power mode value + /* ------------------------------------------------------------------------ * Boolean avoidance * @@ -303,11 +306,7 @@ struct HwcDisplayVariant { static constexpr HWC2::DisplayType HWC_DISPLAY_TYPE = hwcDisplayType; // The HWC active configuration id - // TODO(b/69807179): SurfaceFlinger does not correctly get the active - // config. Once it does, change this to non-zero so that it is properly - // covered. - // static constexpr int HWC_ACTIVE_CONFIG_ID = 2001; - static constexpr int HWC_ACTIVE_CONFIG_ID = 0; + static constexpr int HWC_ACTIVE_CONFIG_ID = 2001; static void injectPendingHotplugEvent(DisplayTransactionTest* test, HWC2::Connection connection) { @@ -359,13 +358,11 @@ struct HwcDisplayVariant { // Called by tests to set up HWC call expectations static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE))); + .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE))); } }; struct NonHwcDisplayVariant { - static constexpr int HWC_ACTIVE_CONFIG_ID = 0; - static void injectHwcDisplay(DisplayTransactionTest*) {} static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) { @@ -581,48 +578,118 @@ struct HdrNotSupportedVariant { } }; +struct NonHwcPerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = 0; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_, _)).Times(0); + } +}; + +template <typename Display> +struct NoPerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = 0; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>()), + Return(Error::NONE))); + } +}; + +template <typename Display> +struct Smpte2086PerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({ + PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X, + PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, + PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X, + PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y, + PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X, + PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y, + PerFrameMetadataKey::WHITE_POINT_X, + PerFrameMetadataKey::WHITE_POINT_Y, + PerFrameMetadataKey::MAX_LUMINANCE, + PerFrameMetadataKey::MIN_LUMINANCE, + })), + Return(Error::NONE))); + } +}; + +template <typename Display> +struct Cta861_3_PerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({ + PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL, + PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, + })), + Return(Error::NONE))); + } +}; + /* ------------------------------------------------------------------------ * Typical display configurations to test */ -template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy> +template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy, + typename PerFrameMetadataSupportPolicy> struct Case { using Display = DisplayPolicy; using WideColorSupport = WideColorSupportPolicy; using HdrSupport = HdrSupportPolicy; + using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy; }; using SimplePrimaryDisplayCase = Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - HdrNotSupportedVariant<PrimaryDisplayVariant>>; + HdrNotSupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; using SimpleExternalDisplayCase = Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>, - HdrNotSupportedVariant<ExternalDisplayVariant>>; + HdrNotSupportedVariant<ExternalDisplayVariant>, + NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>; using SimpleTertiaryDisplayCase = Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>, - HdrNotSupportedVariant<TertiaryDisplayVariant>>; + HdrNotSupportedVariant<TertiaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>; using NonHwcVirtualDisplayCase = Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>, - WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant>; + WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant, + NonHwcPerFrameMetadataSupportVariant>; using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>; using HwcVirtualDisplayCase = Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant, - HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>>; + HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>, + NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>; using WideColorP3ColorimetricDisplayCase = Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>, - HdrNotSupportedVariant<PrimaryDisplayVariant>>; + HdrNotSupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; using Hdr10DisplayCase = Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - Hdr10SupportedVariant<PrimaryDisplayVariant>>; + Hdr10SupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; using HdrHlgDisplayCase = Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - HdrHlgSupportedVariant<PrimaryDisplayVariant>>; + HdrHlgSupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; using HdrDolbyVisionDisplayCase = Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>>; + HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using HdrSmpte2086DisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + HdrNotSupportedVariant<PrimaryDisplayVariant>, + Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using HdrCta861_3_DisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + HdrNotSupportedVariant<PrimaryDisplayVariant>, + Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfiguredVariant, - NonHwcDisplayHdrSupportVariant>; - + NonHwcDisplayHdrSupportVariant, + NoPerFrameMetadataSupportVariant<InvalidDisplayVariant>>; /* ------------------------------------------------------------------------ * * SurfaceFlinger::onHotplugReceived @@ -960,12 +1027,10 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { // Various native window calls will be made. Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this); - - // TODO(b/69807179): SurfaceFlinger does not correctly get the active config. - // Case::Display::setupHwcGetActiveConfigCallExpectations(this) - + Case::Display::setupHwcGetActiveConfigCallExpectations(this); Case::WideColorSupport::setupComposerCallExpectations(this); Case::HdrSupport::setupComposerCallExpectations(this); + Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); // -------------------------------------------------------------------- // Invocation @@ -986,7 +1051,12 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support()); EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport()); EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport()); - EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveConfig()); + // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are + // remapped, and the test only ever sets up one config. If there were an error + // looking up the remapped index, device->getActiveConfig() would be -1 instead. + EXPECT_EQ(0, device->getActiveConfig()); + EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS, + device->getSupportedPerFrameMetadata()); } TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) { @@ -1025,6 +1095,14 @@ TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) { setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>(); } +TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) { + setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) { + setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>(); +} + /* ------------------------------------------------------------------------ * SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded) */ @@ -1082,6 +1160,7 @@ void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessin Case::WideColorSupport::setupComposerCallExpectations(this); Case::HdrSupport::setupComposerCallExpectations(this); + Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, true)).Times(1); @@ -1421,6 +1500,7 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this); Case::WideColorSupport::setupComposerCallExpectations(this); Case::HdrSupport::setupComposerCallExpectations(this); + Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); // -------------------------------------------------------------------- // Invocation @@ -2276,5 +2356,446 @@ TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) { EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency); } +/* ------------------------------------------------------------------------ + * SurfaceFlinger::setPowerModeInternal + */ + +// Used when we simulate a display that supports doze. +struct DozeIsSupportedVariant { + static constexpr bool DOZE_SUPPORTED = true; + static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE = + IComposerClient::PowerMode::DOZE; + static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND = + IComposerClient::PowerMode::DOZE_SUSPEND; +}; + +// Used when we simulate a display that does not support doze. +struct DozeNotSupportedVariant { + static constexpr bool DOZE_SUPPORTED = false; + static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE = + IComposerClient::PowerMode::ON; + static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND = + IComposerClient::PowerMode::ON; +}; + +struct EventThreadBaseSupportedVariant { + static void setupEventAndEventControlThreadNoCallExpectations(DisplayTransactionTest* test) { + // The event control thread should not be notified. + EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(_)).Times(0); + + // The event thread should not be notified. + EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0); + EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0); + } +}; + +struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant { + static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) { + // These calls are only expected for the primary display. + + // Instead expect no calls. + setupEventAndEventControlThreadNoCallExpectations(test); + } + + static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) { + // These calls are only expected for the primary display. + + // Instead expect no calls. + setupEventAndEventControlThreadNoCallExpectations(test); + } +}; + +struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { + static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) { + // The event control thread should be notified to enable vsyncs + EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(true)).Times(1); + + // The event thread should be notified that the screen was acquired. + EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1); + } + + static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) { + // There should be a call to setVsyncEnabled(false) + EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(false)).Times(1); + + // The event thread should not be notified that the screen was released. + EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1); + } +}; + +// -------------------------------------------------------------------- +// Note: +// +// There are a large number of transitions we could test, however we only test a +// selected subset which provides complete test coverage of the implementation. +// -------------------------------------------------------------------- + +template <int initialPowerMode, int targetPowerMode> +struct TransitionVariantCommon { + static constexpr auto INITIAL_POWER_MODE = initialPowerMode; + static constexpr auto TARGET_POWER_MODE = targetPowerMode; + + static void verifyPostconditions(DisplayTransactionTest*) {} +}; + +struct TransitionOffToOnVariant + : public TransitionVariantCommon<HWC_POWER_MODE_OFF, HWC_POWER_MODE_NORMAL> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); + Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::setupRepaintEverythingCallExpectations(test); + } + + static void verifyPostconditions(DisplayTransactionTest* test) { + EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); + EXPECT_TRUE(test->mFlinger.getHasPoweredOff()); + } +}; + +struct TransitionOffToDozeSuspendVariant + : public TransitionVariantCommon<HWC_POWER_MODE_OFF, HWC_POWER_MODE_DOZE_SUSPEND> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); + Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); + Case::setupRepaintEverythingCallExpectations(test); + } + + static void verifyPostconditions(DisplayTransactionTest* test) { + EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); + EXPECT_TRUE(test->mFlinger.getHasPoweredOff()); + } +}; + +struct TransitionOnToOffVariant + : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_OFF> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); + } + + static void verifyPostconditions(DisplayTransactionTest* test) { + EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); + } +}; + +struct TransitionDozeSuspendToOffVariant + : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_OFF> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); + } + + static void verifyPostconditions(DisplayTransactionTest* test) { + EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); + } +}; + +struct TransitionOnToDozeVariant + : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_DOZE> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); + Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); + } +}; + +struct TransitionDozeSuspendToDozeVariant + : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_DOZE> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); + } +}; + +struct TransitionDozeToOnVariant + : public TransitionVariantCommon<HWC_POWER_MODE_DOZE, HWC_POWER_MODE_NORMAL> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); + } +}; + +struct TransitionDozeSuspendToOnVariant + : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_NORMAL> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); + } +}; + +struct TransitionOnToDozeSuspendVariant + : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_DOZE_SUSPEND> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); + } +}; + +struct TransitionOnToUnknownVariant + : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_LEET> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); + Case::setupNoComposerPowerModeCallExpectations(test); + } +}; + +// -------------------------------------------------------------------- +// Note: +// +// Rather than testing the cartesian product of of +// DozeIsSupported/DozeNotSupported with all other options, we use one for one +// display type, and the other for another display type. +// -------------------------------------------------------------------- + +template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant, + typename TransitionVariant> +struct DisplayPowerCase { + using Display = DisplayVariant; + using Doze = DozeVariant; + using EventThread = EventThreadVariant; + using Transition = TransitionVariant; + + static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, int mode) { + Display::injectHwcDisplay(test); + auto display = Display::makeFakeExistingDisplayInjector(test); + display.inject(); + display.mutableDisplayDevice()->setPowerMode(mode); + return display; + } + + static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) { + test->mFlinger.mutablePrimaryHWVsyncEnabled() = enabled; + } + + static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1); + } + + static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test, int mode) { + EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true)); + EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, mode)).Times(1); + } + + static void setupComposerCallExpectations(DisplayTransactionTest* test, + IComposerClient::PowerMode mode) { + // Any calls to get the active config will return a default value. + EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID), + Return(Error::NONE))); + + // Any calls to get whether the display supports dozing will return the value set by the + // policy variant. + EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE))); + + EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1); + } + + static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0); + } +}; + +// A sample configuration for the primary display. +// In addition to having event thread support, we emulate doze support. +template <typename TransitionVariant> +using PrimaryDisplayPowerCase = DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant, + EventThreadIsSupportedVariant, TransitionVariant>; + +// A sample configuration for the external display. +// In addition to not having event thread support, we emulate not having doze +// support. +template <typename TransitionVariant> +using ExternalDisplayPowerCase = + DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant, + EventThreadNotSupportedVariant, TransitionVariant>; + +class SetPowerModeInternalTest : public DisplayTransactionTest { +public: + template <typename Case> + void transitionDisplayCommon(); +}; + +template <int PowerMode> +struct PowerModeInitialVSyncEnabled : public std::false_type {}; + +template <> +struct PowerModeInitialVSyncEnabled<HWC_POWER_MODE_NORMAL> : public std::true_type {}; + +template <> +struct PowerModeInitialVSyncEnabled<HWC_POWER_MODE_DOZE> : public std::true_type {}; + +template <typename Case> +void SetPowerModeInternalTest::transitionDisplayCommon() { + // -------------------------------------------------------------------- + // Preconditions + + auto display = + Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE); + Case::setInitialPrimaryHWVsyncEnabled(this, + PowerModeInitialVSyncEnabled< + Case::Transition::INITIAL_POWER_MODE>::value); + + // -------------------------------------------------------------------- + // Call Expectations + + Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE); + Case::Transition::template setupCallExpectations<Case>(this); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), + Case::Transition::TARGET_POWER_MODE); + + // -------------------------------------------------------------------- + // Postconditions + + Case::Transition::verifyPostconditions(this); +} + +TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A primary display device is set up + Case::Display::injectHwcDisplay(this); + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The diplay is already set to HWC_POWER_MODE_NORMAL + display.mutableDisplayDevice()->setPowerMode(HWC_POWER_MODE_NORMAL); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL); + + // -------------------------------------------------------------------- + // Postconditions + + EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode()); +} + +TEST_F(SetPowerModeInternalTest, setPowerModeInternalJustSetsInternalStateIfVirtualDisplay) { + using Case = HwcVirtualDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // We need to resize this so that the HWC thinks the virtual display + // is something it created. + mFlinger.mutableHwcDisplayData().resize(3); + + // A virtual display device is set up + Case::Display::injectHwcDisplay(this); + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display is set to HWC_POWER_MODE_OFF + getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_OFF); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL); + + // -------------------------------------------------------------------- + // Postconditions + + EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode()); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>(); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp new file mode 100644 index 0000000000..b34645463f --- /dev/null +++ b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <log/log.h> + +#include "AsyncCallRecorder.h" +#include "EventControlThread.h" + +namespace android { +namespace { + +using namespace std::chrono_literals; +using testing::_; + +class EventControlThreadTest : public testing::Test { +protected: + EventControlThreadTest(); + ~EventControlThreadTest() override; + + void createThread(); + + void expectVSyncEnableCallbackCalled(bool enable); + + AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; + + std::unique_ptr<EventControlThread> mThread; +}; + +EventControlThreadTest::EventControlThreadTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +EventControlThreadTest::~EventControlThreadTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +void EventControlThreadTest::createThread() { + mThread = std::make_unique<android::impl::EventControlThread>( + mVSyncSetEnabledCallRecorder.getInvocable()); +} + +void EventControlThreadTest::expectVSyncEnableCallbackCalled(bool expectedEnabled) { + auto args = mVSyncSetEnabledCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(std::get<0>(args.value()), expectedEnabled); +} + +/* ------------------------------------------------------------------------ + * Test cases + */ + +TEST_F(EventControlThreadTest, signalsVSyncDisabledOnStartup) { + createThread(); + + // On thread start, there should be an automatic explicit call to disable + // vsyncs + expectVSyncEnableCallbackCalled(false); +} + +TEST_F(EventControlThreadTest, signalsVSyncDisabledOnce) { + createThread(); + expectVSyncEnableCallbackCalled(false); + + mThread->setVsyncEnabled(false); + + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); +} + +TEST_F(EventControlThreadTest, signalsVSyncEnabledThenDisabled) { + createThread(); + expectVSyncEnableCallbackCalled(false); + + mThread->setVsyncEnabled(true); + + expectVSyncEnableCallbackCalled(true); + + mThread->setVsyncEnabled(false); + + expectVSyncEnableCallbackCalled(false); +} + +TEST_F(EventControlThreadTest, signalsVSyncEnabledOnce) { + createThread(); + expectVSyncEnableCallbackCalled(false); + + mThread->setVsyncEnabled(true); + + expectVSyncEnableCallbackCalled(true); + + mThread->setVsyncEnabled(true); + + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp new file mode 100644 index 0000000000..80fdb80264 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <log/log.h> + +#include <utils/Errors.h> + +#include "AsyncCallRecorder.h" +#include "EventThread.h" + +using namespace std::chrono_literals; +using namespace std::placeholders; + +using testing::_; +using testing::Invoke; + +namespace android { +namespace { + +class MockVSyncSource : public VSyncSource { +public: + MOCK_METHOD1(setVSyncEnabled, void(bool)); + MOCK_METHOD1(setCallback, void(VSyncSource::Callback*)); + MOCK_METHOD1(setPhaseOffset, void(nsecs_t)); +}; + +} // namespace + +class EventThreadTest : public testing::Test { +protected: + class MockEventThreadConnection : public android::impl::EventThread::Connection { + public: + explicit MockEventThreadConnection(android::impl::EventThread* eventThread) + : android::impl::EventThread::Connection(eventThread) {} + MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event)); + }; + + using ConnectionEventRecorder = + AsyncCallRecorderWithCannedReturn<status_t (*)(const DisplayEventReceiver::Event&)>; + + EventThreadTest(); + ~EventThreadTest() override; + + void createThread(); + sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder); + + void expectVSyncSetEnabledCallReceived(bool expectedState); + void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset); + VSyncSource::Callback* expectVSyncSetCallbackCallReceived(); + void expectInterceptCallReceived(nsecs_t expectedTimestamp); + void expectVsyncEventReceivedByConnection(const char* name, + ConnectionEventRecorder& connectionEventRecorder, + nsecs_t expectedTimestamp, unsigned expectedCount); + void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount); + void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected); + + AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; + AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder; + AsyncCallRecorder<void (*)()> mResyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; + ConnectionEventRecorder mConnectionEventCallRecorder{0}; + + MockVSyncSource mVSyncSource; + std::unique_ptr<android::impl::EventThread> mThread; + sp<MockEventThreadConnection> mConnection; +}; + +EventThreadTest::EventThreadTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + + EXPECT_CALL(mVSyncSource, setVSyncEnabled(_)) + .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable())); + + EXPECT_CALL(mVSyncSource, setCallback(_)) + .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable())); + + EXPECT_CALL(mVSyncSource, setPhaseOffset(_)) + .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable())); + + createThread(); + mConnection = createConnection(mConnectionEventCallRecorder); +} + +EventThreadTest::~EventThreadTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +void EventThreadTest::createThread() { + mThread = + std::make_unique<android::impl::EventThread>(&mVSyncSource, + mResyncCallRecorder.getInvocable(), + mInterceptVSyncCallRecorder.getInvocable(), + "unit-test-event-thread"); +} + +sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection( + ConnectionEventRecorder& recorder) { + sp<MockEventThreadConnection> connection = new MockEventThreadConnection(mThread.get()); + EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable())); + return connection; +} + +void EventThreadTest::expectVSyncSetEnabledCallReceived(bool expectedState) { + auto args = mVSyncSetEnabledCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedState, std::get<0>(args.value())); +} + +void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset) { + auto args = mVSyncSetPhaseOffsetCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value())); +} + +VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() { + auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall(); + return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr; +} + +void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) { + auto args = mInterceptVSyncCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedTimestamp, std::get<0>(args.value())); +} + +void EventThreadTest::expectVsyncEventReceivedByConnection( + const char* name, ConnectionEventRecorder& connectionEventRecorder, + nsecs_t expectedTimestamp, unsigned expectedCount) { + auto args = connectionEventRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()) << name << " did not receive an event for timestamp " + << expectedTimestamp; + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_VSYNC, event.header.type) + << name << " did not get the correct event for timestamp " << expectedTimestamp; + EXPECT_EQ(expectedTimestamp, event.header.timestamp) + << name << " did not get the expected timestamp for timestamp " << expectedTimestamp; + EXPECT_EQ(expectedCount, event.vsync.count) + << name << " did not get the expected count for timestamp " << expectedTimestamp; +} + +void EventThreadTest::expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, + unsigned expectedCount) { + expectVsyncEventReceivedByConnection("mConnectionEventCallRecorder", + mConnectionEventCallRecorder, expectedTimestamp, + expectedCount); +} + +void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType, + bool expectedConnected) { + auto args = mConnectionEventCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type); + EXPECT_EQ(static_cast<unsigned>(expectedDisplayType), event.header.id); + EXPECT_EQ(expectedConnected, event.hotplug.connected); +} + +namespace { + +/* ------------------------------------------------------------------------ + * Test cases + */ + +TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) { + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value()); +} + +TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { + // Signal that we want the next vsync event to be posted to the connection + mThread->requestNextVsync(mConnection); + + // EventThread should immediately request a resync. + EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value()); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // Use the received callback to signal a first vsync event. + // The interceptor should receive the event, as well as the connection. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + expectVsyncEventReceivedByConnection(123, 1u); + + // Use the received callback to signal a second vsync event. + // The interceptor should receive the event, but the the connection should + // not as it was only interested in the first. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // EventThread should also detect that at this point that it does not need + // any more vsync events, and should disable their generation. + expectVSyncSetEnabledCallReceived(false); +} + +TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) { + // Create a first connection, register it, and request a vsync rate of zero. + ConnectionEventRecorder firstConnectionEventRecorder{0}; + sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder); + mThread->setVsyncRate(0, firstConnection); + + // By itself, this should not enable vsync events + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value()); + + // However if there is another connection which wants events at a nonzero rate..... + ConnectionEventRecorder secondConnectionEventRecorder{0}; + sp<MockEventThreadConnection> secondConnection = + createConnection(secondConnectionEventRecorder); + mThread->setVsyncRate(1, secondConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // Send a vsync event. EventThread should then make a call to the + // interceptor, and the second connection. The first connection should not + // get the event. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value()); + expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123, + 1u); +} + +TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) { + mThread->setVsyncRate(1, mConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // Send a vsync event. EventThread should then make a call to the + // interceptor, and the connection. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + expectVsyncEventReceivedByConnection(123, 1u); + + // A second event should go to the same places. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + expectVsyncEventReceivedByConnection(456, 2u); + + // A third event should go to the same places. + callback->onVSyncEvent(789); + expectInterceptCallReceived(789); + expectVsyncEventReceivedByConnection(789, 3u); +} + +TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) { + mThread->setVsyncRate(2, mConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // The first event will be seen by the interceptor, and not the connection. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // The second event will be seen by the interceptor and the connection. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + expectVsyncEventReceivedByConnection(456, 2u); + + // The third event will be seen by the interceptor, and not the connection. + callback->onVSyncEvent(789); + expectInterceptCallReceived(789); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // The fourth event will be seen by the interceptor and the connection. + callback->onVSyncEvent(101112); + expectInterceptCallReceived(101112); + expectVsyncEventReceivedByConnection(101112, 4u); +} + +TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) { + mThread->setVsyncRate(1, mConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // Destroy the only (strong) reference to the connection. + mConnection = nullptr; + + // The first event will be seen by the interceptor, and not the connection. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // EventThread should disable vsync callbacks + expectVSyncSetEnabledCallReceived(false); +} + +TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { + ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY}; + sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder); + mThread->setVsyncRate(1, errorConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // The first event will be seen by the interceptor, and by the connection, + // which then returns an error. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); + + // A subsequent event will be seen by the interceptor and not by the + // connection. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value()); + + // EventThread should disable vsync callbacks with the second event + expectVSyncSetEnabledCallReceived(false); +} + +TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { + ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK}; + sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder); + mThread->setVsyncRate(1, errorConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // The first event will be seen by the interceptor, and by the connection, + // which then returns an non-fatal error. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); + + // A subsequent event will be seen by the interceptor, and by the connection, + // which still then returns an non-fatal error. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u); + + // EventThread will not disable vsync callbacks as the errors are non-fatal. + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); +} + +TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) { + mThread->setPhaseOffset(321); + expectVSyncSetPhaseOffsetCallReceived(321); +} + +TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false); + expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false); +} + +TEST_F(EventThreadTest, postHotplugPrimaryConnect) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true); + expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true); +} + +TEST_F(EventThreadTest, postHotplugExternalDisconnect) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false); + expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false); +} + +TEST_F(EventThreadTest, postHotplugExternalConnect) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true); + expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true); +} + +TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index a4e73613cd..f1556d8bc7 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -89,11 +89,19 @@ public: auto onInitializeDisplays() { return mFlinger->onInitializeDisplays(); } + auto setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, bool stateLockHeld = false) { + return mFlinger->setPowerModeInternal(hw, mode, stateLockHeld); + } + /* ------------------------------------------------------------------------ * Read-only access to private data to assert post-conditions. */ const auto& getAnimFrameTracker() const { return mFlinger->mAnimFrameTracker; } + const auto& getHasPoweredOff() const { return mFlinger->mHasPoweredOff; } + const auto& getHWVsyncAvailable() const { return mFlinger->mHWVsyncAvailable; } + const auto& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; } + const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; } /* ------------------------------------------------------------------------ |