diff options
-rw-r--r-- | include/gui/FrameTimestamps.h | 13 | ||||
-rw-r--r-- | include/gui/Surface.h | 11 | ||||
-rw-r--r-- | include/ui/FenceTime.h | 47 | ||||
-rw-r--r-- | libs/gui/FrameTimestamps.cpp | 59 | ||||
-rw-r--r-- | libs/gui/Surface.cpp | 29 | ||||
-rw-r--r-- | libs/gui/tests/Surface_test.cpp | 935 | ||||
-rw-r--r-- | libs/ui/FenceTime.cpp | 84 |
7 files changed, 1128 insertions, 50 deletions
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h index 8a3fa39956..7d627061c0 100644 --- a/include/gui/FrameTimestamps.h +++ b/include/gui/FrameTimestamps.h @@ -116,13 +116,22 @@ class ProducerFrameEventHistory : public FrameEventHistory { public: ~ProducerFrameEventHistory() override; - void updateAcquireFence( + // virtual for testing. + virtual void updateAcquireFence( uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire); void applyDelta(const FrameEventHistoryDelta& delta); void updateSignalTimes(); -private: +protected: + void applyFenceDelta(FenceTimeline* timeline, + std::shared_ptr<FenceTime>* dst, + const FenceTime::Snapshot& src) const; + + // virtual for testing. + virtual std::shared_ptr<FenceTime> createFenceTime( + const sp<Fence>& fence) const; + size_t mAcquireOffset{0}; // The consumer updates it's timelines in Layer and SurfaceFlinger since diff --git a/include/gui/Surface.h b/include/gui/Surface.h index 451bd68f66..43b8d965d2 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -33,6 +33,8 @@ struct ANativeWindow_Buffer; namespace android { +class ISurfaceComposer; + /* * An implementation of ANativeWindow that feeds graphics buffers into a * BufferQueue. @@ -66,7 +68,8 @@ public: * the controlledByApp flag indicates that this Surface (producer) is * controlled by the application. This flag is used at connect time. */ - explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false); + explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, + bool controlledByApp = false); /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this * Surface was created with. Usually it's an error to use the @@ -152,6 +155,9 @@ public: protected: virtual ~Surface(); + // Virtual for testing. + virtual sp<ISurfaceComposer> composerService() const; + private: // can't be copied Surface& operator = (const Surface& rhs); @@ -245,7 +251,6 @@ protected: enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; -private: void querySupportedTimestampsLocked() const; void freeAllBuffers(); @@ -399,7 +404,7 @@ private: // A cached copy of the FrameEventHistory maintained by the consumer. bool mEnableFrameTimestamps = false; - ProducerFrameEventHistory mFrameEventHistory; + std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory; }; namespace view { diff --git a/include/ui/FenceTime.h b/include/ui/FenceTime.h index 27cc720028..871fcf2dfe 100644 --- a/include/ui/FenceTime.h +++ b/include/ui/FenceTime.h @@ -24,13 +24,17 @@ #include <atomic> #include <mutex> #include <queue> +#include <unordered_map> namespace android { +class FenceToFenceTimeMap; + // A wrapper around fence that only implements isValid and getSignalTime. // It automatically closes the fence in a thread-safe manner once the signal // time is known. class FenceTime { +friend class FenceToFenceTimeMap; public: // An atomic snapshot of the FenceTime that is flattenable. // @@ -107,15 +111,22 @@ public: // Returns a snapshot of the FenceTime in its current state. Snapshot getSnapshot() const; + void signalForTest(nsecs_t signalTime); + // Override new and delete since this needs 8-byte alignment, which // is not guaranteed on x86. static void* operator new(size_t nbytes) noexcept; static void operator delete(void *p); private: + // For tests only. If forceValidForTest is true, then getSignalTime will + // never return SIGNAL_TIME_INVALID and isValid will always return true. + FenceTime(const sp<Fence>& fence, bool forceValidForTest); + enum class State { VALID, INVALID, + FORCED_VALID_FOR_TEST, }; const State mState{State::INVALID}; @@ -156,6 +167,42 @@ private: std::queue<std::weak_ptr<FenceTime>> mQueue; }; +// Used by test code to create or get FenceTimes for a given Fence. +// +// By design, Fences cannot be signaled from user space. However, this class +// allows test code to set the apparent signalTime of a Fence and +// have it be visible to all FenceTimes. Release code should not use +// FenceToFenceTimeMap. +// +// FenceToFenceTimeMap keeps a weak reference to the FenceTime and automatically +// garbage collects entries every time a new FenceTime is created to avoid +// leaks. This prevents us from having to make the Fence destructor +// automatically notify that the underlying fence has been destroyed, which +// would affect release code paths. Garbage collecting so often is inefficient, +// but acceptable for testing. +// +// Since FenceTimes maintain a strong reference to underlying Fences, there +// should not be any aliasing issues where a new Fence happens to have the same +// address as a previous Fence; the previous entry will be garbage collected +// before the new one is added. +class FenceToFenceTimeMap { +public: + // Create a new FenceTime with that wraps the provided Fence. + std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence); + + // Signals all FenceTimes created through this class that are wrappers + // around |fence|. + void signalAllForTest(const sp<Fence>& fence, nsecs_t signalTime); + +private: + // Cleans up the entries that no longer have a strong reference. + void garbageCollectLocked(); + + mutable std::mutex mMutex; + std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap; +}; + + }; // namespace android #endif // ANDROID_FENCE_TIME_H diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp index c9b894833b..46ca97470a 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp @@ -241,33 +241,6 @@ void ProducerFrameEventHistory::updateAcquireFence( } } -static void applyFenceDelta(FenceTimeline* timeline, - std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) { - if (CC_UNLIKELY(dst == nullptr)) { - ALOGE("applyFenceDelta: dst is null."); - return; - } - - switch (src.state) { - case FenceTime::Snapshot::State::EMPTY: - return; - case FenceTime::Snapshot::State::FENCE: - if (CC_UNLIKELY((*dst)->isValid())) { - ALOGE("applyFenceDelta: Unexpected fence."); - } - *dst = std::make_shared<FenceTime>(src.fence); - timeline->push(*dst); - return; - case FenceTime::Snapshot::State::SIGNAL_TIME: - if ((*dst)->isValid()) { - (*dst)->applyTrustedSnapshot(src); - } else { - *dst = std::make_shared<FenceTime>(src.signalTime); - } - return; - } -} - void ProducerFrameEventHistory::applyDelta( const FrameEventHistoryDelta& delta) { for (auto& d : delta.mDeltas) { @@ -320,6 +293,38 @@ void ProducerFrameEventHistory::updateSignalTimes() { mReleaseTimeline.updateSignalTimes(); } +void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline, + std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) const { + if (CC_UNLIKELY(dst == nullptr)) { + ALOGE("applyFenceDelta: dst is null."); + return; + } + + switch (src.state) { + case FenceTime::Snapshot::State::EMPTY: + return; + case FenceTime::Snapshot::State::FENCE: + if (CC_UNLIKELY((*dst)->isValid())) { + ALOGE("applyFenceDelta: Unexpected fence."); + } + *dst = createFenceTime(src.fence); + timeline->push(*dst); + return; + case FenceTime::Snapshot::State::SIGNAL_TIME: + if ((*dst)->isValid()) { + (*dst)->applyTrustedSnapshot(src); + } else { + *dst = std::make_shared<FenceTime>(src.signalTime); + } + return; + } +} + +std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime( + const sp<Fence>& fence) const { + return std::make_shared<FenceTime>(fence); +} + // ============================================================================ // ConsumerFrameEventHistory diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index a172c32522..ead8ecb3b4 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -52,7 +52,8 @@ Surface::Surface( mQueriedSupportedTimestamps(false), mFrameTimestampsSupportsPresent(false), mFrameTimestampsSupportsRetire(false), - mEnableFrameTimestamps(false) + mEnableFrameTimestamps(false), + mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; @@ -96,6 +97,10 @@ Surface::~Surface() { } } +sp<ISurfaceComposer> Surface::composerService() const { + return ComposerService::getComposerService(); +} + sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const { return mGraphicBufferProducer; } @@ -203,7 +208,7 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, return BAD_VALUE; } - FrameEvents* events = mFrameEventHistory.getFrame(frameNumber); + FrameEvents* events = mFrameEventHistory->getFrame(frameNumber); if (events == nullptr) { // If the entry isn't available in the producer, it's definitely not // available in the consumer. @@ -216,8 +221,8 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, outDisplayPresentTime, outDisplayRetireTime, outReleaseTime)) { FrameEventHistoryDelta delta; mGraphicBufferProducer->getFrameTimestamps(&delta); - mFrameEventHistory.applyDelta(delta); - events = mFrameEventHistory.getFrame(frameNumber); + mFrameEventHistory->applyDelta(delta); + events = mFrameEventHistory->getFrame(frameNumber); } if (events == nullptr) { @@ -396,7 +401,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { } if (enableFrameTimestamps) { - mFrameEventHistory.applyDelta(frameTimestamps); + mFrameEventHistory->applyDelta(frameTimestamps); } if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { @@ -590,16 +595,16 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { } if (mEnableFrameTimestamps) { - mFrameEventHistory.applyDelta(output.frameTimestamps); + mFrameEventHistory->applyDelta(output.frameTimestamps); // Update timestamps with the local acquire fence. // The consumer doesn't send it back to prevent us from having two // file descriptors of the same fence. - mFrameEventHistory.updateAcquireFence(mNextFrameNumber, + mFrameEventHistory->updateAcquireFence(mNextFrameNumber, std::make_shared<FenceTime>(std::move(fence))); // Cache timestamps of signaled fences so we can close their file // descriptors. - mFrameEventHistory.updateSignalTimes(); + mFrameEventHistory->updateSignalTimes(); } mLastFrameNumber = mNextFrameNumber; @@ -638,8 +643,7 @@ void Surface::querySupportedTimestampsLocked() const { mQueriedSupportedTimestamps = true; std::vector<FrameEvent> supportedFrameTimestamps; - sp<ISurfaceComposer> composer(ComposerService::getComposerService()); - status_t err = composer->getSupportedFrameTimestamps( + status_t err = composerService()->getSupportedFrameTimestamps( &supportedFrameTimestamps); if (err != NO_ERROR) { @@ -668,9 +672,8 @@ int Surface::query(int what, int* value) const { } break; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { - sp<ISurfaceComposer> composer( - ComposerService::getComposerService()); - if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) { + if (composerService()->authenticateSurfaceTexture( + mGraphicBufferProducer)) { *value = 1; } else { *value = 0; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 66e1bb61b2..22069023c7 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -19,22 +19,28 @@ #include <gtest/gtest.h> #include <binder/IMemory.h> +#include <binder/ProcessState.h> +#include <gui/IDisplayEventConnection.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/BufferItemConsumer.h> +#include <private/gui/ComposerService.h> #include <ui/Rect.h> #include <utils/String8.h> -#include <private/gui/ComposerService.h> -#include <binder/ProcessState.h> - +#include <limits> #include <thread> namespace android { using namespace std::chrono_literals; +class FakeSurfaceComposer; +class FakeProducerFrameEventHistory; + +static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max(); + class SurfaceTest : public ::testing::Test { protected: @@ -102,7 +108,8 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { BufferQueue::createBufferQueue(&producer, &consumer); sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + sp<IBinder> display(sf->getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain)); ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(), 64, 64, 0, 0x7fffffff, false)); @@ -272,4 +279,924 @@ TEST_F(SurfaceTest, DynamicSetBufferCount) { ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); } + +class FakeConsumer : public BnConsumerListener { +public: + void onFrameAvailable(const BufferItem& /*item*/) override {} + void onBuffersReleased() override {} + void onSidebandStreamChanged() override {} + + void addAndGetFrameTimestamps( + const NewFrameEventsEntry* newTimestamps, + FrameEventHistoryDelta* outDelta) override { + if (newTimestamps) { + if (mGetFrameTimestampsEnabled) { + EXPECT_GT(mNewFrameEntryOverride.frameNumber, 0u) << + "Test should set mNewFrameEntryOverride before queuing " + "a frame."; + EXPECT_EQ(newTimestamps->frameNumber, + mNewFrameEntryOverride.frameNumber) << + "Test attempting to add NewFrameEntryOverride with " + "incorrect frame number."; + mFrameEventHistory.addQueue(mNewFrameEntryOverride); + mNewFrameEntryOverride.frameNumber = 0; + } + mAddFrameTimestampsCount++; + mLastAddedFrameNumber = newTimestamps->frameNumber; + } + if (outDelta) { + mFrameEventHistory.getAndResetDelta(outDelta); + mGetFrameTimestampsCount++; + } + mAddAndGetFrameTimestampsCallCount++; + } + + bool mGetFrameTimestampsEnabled = false; + + ConsumerFrameEventHistory mFrameEventHistory; + int mAddAndGetFrameTimestampsCallCount = 0; + int mAddFrameTimestampsCount = 0; + int mGetFrameTimestampsCount = 0; + uint64_t mLastAddedFrameNumber = NO_FRAME_INDEX; + + NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr }; +}; + + +class FakeSurfaceComposer : public ISurfaceComposer{ +public: + ~FakeSurfaceComposer() override {} + + void setSupportedTimestamps(bool supportsPresent, bool supportsRetire) { + mSupportsPresent = supportsPresent; + mSupportsRetire = supportsRetire; + } + + sp<ISurfaceComposerClient> createConnection() override { return nullptr; } + sp<IGraphicBufferAlloc> createGraphicBufferAlloc() override { + return nullptr; + } + sp<IDisplayEventConnection> createDisplayEventConnection() override { + return nullptr; + } + sp<IBinder> createDisplay(const String8& /*displayName*/, + bool /*secure*/) override { return nullptr; } + void destroyDisplay(const sp<IBinder>& /*display */) override {} + sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; } + void setTransactionState(const Vector<ComposerState>& /*state*/, + const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/) + override {} + void bootFinished() override {} + bool authenticateSurfaceTexture( + const sp<IGraphicBufferProducer>& /*surface*/) const override { + return false; + } + + status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) + const override { + *outSupported = { + FrameEvent::REQUESTED_PRESENT, + FrameEvent::ACQUIRE, + FrameEvent::FIRST_REFRESH_START, + FrameEvent::GL_COMPOSITION_DONE, + FrameEvent::RELEASE + }; + if (mSupportsPresent) { + outSupported->push_back( + FrameEvent::DISPLAY_PRESENT); + } + if (mSupportsRetire) { + outSupported->push_back( + FrameEvent::DISPLAY_RETIRE); + } + return NO_ERROR; + } + + void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {} + status_t getDisplayConfigs(const sp<IBinder>& /*display*/, + Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; } + status_t getDisplayStats(const sp<IBinder>& /*display*/, + DisplayStatInfo* /*stats*/) override { return NO_ERROR; } + int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; } + status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/) + override { + return NO_ERROR; + } + status_t getDisplayColorModes(const sp<IBinder>& /*display*/, + Vector<android_color_mode_t>* /*outColorModes*/) override { + return NO_ERROR; + } + android_color_mode_t getActiveColorMode(const sp<IBinder>& /*display*/) + override { + return HAL_COLOR_MODE_NATIVE; + } + status_t setActiveColorMode(const sp<IBinder>& /*display*/, + android_color_mode_t /*colorMode*/) override { return NO_ERROR; } + status_t captureScreen(const sp<IBinder>& /*display*/, + const sp<IGraphicBufferProducer>& /*producer*/, + Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, + uint32_t /*minLayerZ*/, uint32_t /*maxLayerZ*/, + bool /*useIdentityTransform*/, + Rotation /*rotation*/) override { return NO_ERROR; } + status_t clearAnimationFrameStats() override { return NO_ERROR; } + status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { + return NO_ERROR; + } + status_t getHdrCapabilities(const sp<IBinder>& /*display*/, + HdrCapabilities* /*outCapabilities*/) const override { + return NO_ERROR; + } + status_t enableVSyncInjections(bool /*enable*/) override { + return NO_ERROR; + } + status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; } + +protected: + IBinder* onAsBinder() override { return nullptr; } + +private: + bool mSupportsPresent{true}; + bool mSupportsRetire{true}; +}; + +class FakeProducerFrameEventHistory : public ProducerFrameEventHistory { +public: + FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap) + : mFenceMap(fenceMap) {} + + ~FakeProducerFrameEventHistory() {} + + void updateAcquireFence(uint64_t frameNumber, + std::shared_ptr<FenceTime>&& acquire) override { + // Verify the acquire fence being added isn't the one from the consumer. + EXPECT_NE(mConsumerAcquireFence, acquire); + // Override the fence, so we can verify this was called by the + // producer after the frame is queued. + ProducerFrameEventHistory::updateAcquireFence(frameNumber, + std::shared_ptr<FenceTime>(mAcquireFenceOverride)); + } + + void setAcquireFenceOverride( + const std::shared_ptr<FenceTime>& acquireFenceOverride, + const std::shared_ptr<FenceTime>& consumerAcquireFence) { + mAcquireFenceOverride = acquireFenceOverride; + mConsumerAcquireFence = consumerAcquireFence; + } + +protected: + std::shared_ptr<FenceTime> createFenceTime(const sp<Fence>& fence) + const override { + return mFenceMap->createFenceTimeForTest(fence); + } + + FenceToFenceTimeMap* mFenceMap{nullptr}; + + std::shared_ptr<FenceTime> mAcquireFenceOverride{FenceTime::NO_FENCE}; + std::shared_ptr<FenceTime> mConsumerAcquireFence{FenceTime::NO_FENCE}; +}; + + +class TestSurface : public Surface { +public: + TestSurface(const sp<IGraphicBufferProducer>& bufferProducer, + FenceToFenceTimeMap* fenceMap) + : Surface(bufferProducer), + mFakeSurfaceComposer(new FakeSurfaceComposer) { + mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap); + mFrameEventHistory.reset(mFakeFrameEventHistory); + } + + ~TestSurface() override {} + + sp<ISurfaceComposer> composerService() const override { + return mFakeSurfaceComposer; + } + +public: + sp<FakeSurfaceComposer> mFakeSurfaceComposer; + + // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory, + // but this raw pointer gives access to test functionality. + FakeProducerFrameEventHistory* mFakeFrameEventHistory; +}; + + +class GetFrameTimestampsTest : public SurfaceTest { +protected: + struct FenceAndFenceTime { + explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap) + : mFence(new Fence), + mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {} + sp<Fence> mFence { nullptr }; + std::shared_ptr<FenceTime> mFenceTime { nullptr }; + }; + + struct RefreshEvents { + RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart) + : mFenceMap(fenceMap), + kStartTime(refreshStart + 1), + kGpuCompositionDoneTime(refreshStart + 2), + kPresentTime(refreshStart + 3) {} + + void signalPostCompositeFences() { + mFenceMap.signalAllForTest( + mGpuCompositionDone.mFence, kGpuCompositionDoneTime); + mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime); + } + + FenceToFenceTimeMap& mFenceMap; + + FenceAndFenceTime mGpuCompositionDone { mFenceMap }; + FenceAndFenceTime mPresent { mFenceMap }; + + const nsecs_t kStartTime; + const nsecs_t kGpuCompositionDoneTime; + const nsecs_t kPresentTime; + }; + + struct FrameEvents { + FrameEvents(FenceToFenceTimeMap& fenceMap, nsecs_t frameStartTime) + : mFenceMap(fenceMap), + kPostedTime(frameStartTime + 100), + kRequestedPresentTime(frameStartTime + 200), + kProducerAcquireTime(frameStartTime + 300), + kConsumerAcquireTime(frameStartTime + 301), + kLatchTime(frameStartTime + 500), + kRetireTime(frameStartTime + 600), + kReleaseTime(frameStartTime + 700), + mRefreshes { + { mFenceMap, frameStartTime + 410 }, + { mFenceMap, frameStartTime + 420 }, + { mFenceMap, frameStartTime + 430 } } {} + + void signalQueueFences() { + mFenceMap.signalAllForTest( + mAcquireConsumer.mFence, kConsumerAcquireTime); + mFenceMap.signalAllForTest( + mAcquireProducer.mFence, kProducerAcquireTime); + } + + void signalRefreshFences() { + for (auto& re : mRefreshes) { + re.signalPostCompositeFences(); + } + } + + void signalReleaseFences() { + mFenceMap.signalAllForTest(mRetire.mFence, kRetireTime); + mFenceMap.signalAllForTest(mRelease.mFence, kReleaseTime); + } + + FenceToFenceTimeMap& mFenceMap; + + FenceAndFenceTime mAcquireConsumer { mFenceMap }; + FenceAndFenceTime mAcquireProducer { mFenceMap }; + FenceAndFenceTime mRetire { mFenceMap }; + FenceAndFenceTime mRelease { mFenceMap }; + + const nsecs_t kPostedTime; + const nsecs_t kRequestedPresentTime; + const nsecs_t kProducerAcquireTime; + const nsecs_t kConsumerAcquireTime; + const nsecs_t kLatchTime; + const nsecs_t kRetireTime; + const nsecs_t kReleaseTime; + + RefreshEvents mRefreshes[3]; + }; + + GetFrameTimestampsTest() : SurfaceTest() {} + + virtual void SetUp() { + SurfaceTest::SetUp(); + + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + mFakeConsumer = new FakeConsumer; + mCfeh = &mFakeConsumer->mFrameEventHistory; + mConsumer->consumerConnect(mFakeConsumer, false); + mConsumer->setConsumerName(String8("TestConsumer")); + mSurface = new TestSurface(mProducer, &mFenceMap); + mWindow = mSurface; + + ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(), + NATIVE_WINDOW_API_CPU)); + native_window_set_buffer_count(mWindow.get(), 4); + } + + void enableFrameTimestamps() { + mFakeConsumer->mGetFrameTimestampsEnabled = true; + native_window_enable_frame_timestamps(mWindow.get(), 1); + mFrameTimestampsEnabled = true; + } + + void resetTimestamps() { + outRequestedPresentTime = -1; + outAcquireTime = -1; + outRefreshStartTime = -1; + outGpuCompositionDoneTime = -1; + outDisplayPresentTime = -1; + outDisplayRetireTime = -1; + outReleaseTime = -1; + } + + void dequeueAndQueue(uint64_t frameIndex) { + int fence = -1; + ANativeWindowBuffer* buffer = nullptr; + ASSERT_EQ(NO_ERROR, + mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); + + int oldAddFrameTimestampsCount = + mFakeConsumer->mAddFrameTimestampsCount; + + FrameEvents* frame = &mFrames[frameIndex]; + uint64_t frameNumber = frameIndex + 1; + + NewFrameEventsEntry fe; + fe.frameNumber = frameNumber; + fe.postedTime = frame->kPostedTime; + fe.requestedPresentTime = frame->kRequestedPresentTime; + fe.acquireFence = frame->mAcquireConsumer.mFenceTime; + mFakeConsumer->mNewFrameEntryOverride = fe; + + mSurface->mFakeFrameEventHistory->setAcquireFenceOverride( + frame->mAcquireProducer.mFenceTime, + frame->mAcquireConsumer.mFenceTime); + + ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); + + EXPECT_EQ(frameNumber, mFakeConsumer->mLastAddedFrameNumber); + + EXPECT_EQ( + oldAddFrameTimestampsCount + (mFrameTimestampsEnabled ? 1 : 0), + mFakeConsumer->mAddFrameTimestampsCount); + } + + void addFrameEvents( + bool gpuComposited, uint64_t iOldFrame, int64_t iNewFrame) { + FrameEvents* oldFrame = + (iOldFrame == NO_FRAME_INDEX) ? nullptr : &mFrames[iOldFrame]; + FrameEvents* newFrame = &mFrames[iNewFrame]; + + uint64_t nOldFrame = iOldFrame + 1; + uint64_t nNewFrame = iNewFrame + 1; + + // Latch, Composite, Retire, and Release the frames in a plausible + // order. Note: The timestamps won't necessarily match the order, but + // that's okay for the purposes of this test. + std::shared_ptr<FenceTime> gpuDoneFenceTime = FenceTime::NO_FENCE; + + mCfeh->addLatch(nNewFrame, newFrame->kLatchTime); + + mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[0].kStartTime); + gpuDoneFenceTime = gpuComposited ? + newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime : + FenceTime::NO_FENCE; + // HWC2 releases the previous buffer after a new latch just before + // calling postComposition. + if (oldFrame != nullptr) { + mCfeh->addRelease(nOldFrame, + std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime)); + } + mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, + newFrame->mRefreshes[0].mPresent.mFenceTime); + + // Retire the previous buffer just after compositing the new buffer. + if (oldFrame != nullptr) { + mCfeh->addRetire(nOldFrame, oldFrame->mRetire.mFenceTime); + } + + mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[1].kStartTime); + gpuDoneFenceTime = gpuComposited ? + newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime : + FenceTime::NO_FENCE; + mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, + newFrame->mRefreshes[1].mPresent.mFenceTime); + mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[2].kStartTime); + gpuDoneFenceTime = gpuComposited ? + newFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime : + FenceTime::NO_FENCE; + mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, + newFrame->mRefreshes[2].mPresent.mFenceTime); + } + + void QueryPresentRetireSupported( + bool displayPresentSupported, bool displayRetireSupported); + void PresentOrRetireUnsupportedNoSyncTest( + bool displayPresentSupported, bool displayRetireSupported); + + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + sp<FakeConsumer> mFakeConsumer; + ConsumerFrameEventHistory* mCfeh; + sp<TestSurface> mSurface; + sp<ANativeWindow> mWindow; + + FenceToFenceTimeMap mFenceMap; + + bool mFrameTimestampsEnabled = false; + + int64_t outRequestedPresentTime = -1; + int64_t outAcquireTime = -1; + int64_t outRefreshStartTime = -1; + int64_t outGpuCompositionDoneTime = -1; + int64_t outDisplayPresentTime = -1; + int64_t outDisplayRetireTime = -1; + int64_t outReleaseTime = -1; + + FrameEvents mFrames[2] { { mFenceMap, 1000 }, { mFenceMap, 2000 } }; +}; + + +// This test verifies that the frame timestamps are not retrieved when not +// explicitly enabled via native_window_enable_frame_timestamps. +// We want to check this to make sure there's no overhead for users +// that don't need the timestamp information. +TEST_F(GetFrameTimestampsTest, DefaultDisabled) { + int fence; + ANativeWindowBuffer* buffer; + + EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify the producer doesn't get frame timestamps piggybacked on dequeue. + ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); + EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify the producer doesn't get frame timestamps piggybacked on queue. + // It is okay that frame timestamps are added in the consumer since it is + // still needed for SurfaceFlinger dumps. + ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); + EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify attempts to get frame timestamps fail. + const uint32_t framesAgo = 0; + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(INVALID_OPERATION, result); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); +} + +// This test verifies that the frame timestamps are retrieved if explicitly +// enabled via native_window_enable_frame_timestamps. +TEST_F(GetFrameTimestampsTest, EnabledSimple) { + enableFrameTimestamps(); + + int fence; + ANativeWindowBuffer* buffer; + + EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify getFrameTimestamps is piggybacked on dequeue. + ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); + EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount); + + NewFrameEventsEntry f1; + f1.frameNumber = 1; + f1.postedTime = mFrames[0].kPostedTime; + f1.requestedPresentTime = mFrames[0].kRequestedPresentTime; + f1.acquireFence = mFrames[0].mAcquireConsumer.mFenceTime; + mSurface->mFakeFrameEventHistory->setAcquireFenceOverride( + mFrames[0].mAcquireProducer.mFenceTime, + mFrames[0].mAcquireConsumer.mFenceTime); + mFakeConsumer->mNewFrameEntryOverride = f1; + mFrames[0].signalQueueFences(); + + // Verify getFrameTimestamps is piggybacked on queue. + ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); + EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount); + EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber); + EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify queries for timestamps that the producer doesn't know about + // triggers a call to see if the consumer has any new timestamps. + const uint32_t framesAgo = 0; + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount); +} + +void GetFrameTimestampsTest::QueryPresentRetireSupported( + bool displayPresentSupported, bool displayRetireSupported) { + mSurface->mFakeSurfaceComposer->setSupportedTimestamps( + displayPresentSupported, displayRetireSupported); + + // Verify supported bits are forwarded. + int supportsPresent = -1; + mWindow.get()->query(mWindow.get(), + NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent); + EXPECT_EQ(displayPresentSupported, supportsPresent); + + int supportsRetire = -1; + mWindow.get()->query(mWindow.get(), + NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE, &supportsRetire); + EXPECT_EQ(displayRetireSupported, supportsRetire); +} + +TEST_F(GetFrameTimestampsTest, QueryPresentSupported) { + QueryPresentRetireSupported(true, false); +} + +TEST_F(GetFrameTimestampsTest, QueryRetireSupported) { + QueryPresentRetireSupported(false, true); +} + +// This test verifies that: +// 1) The timestamps recorded in the consumer's FrameTimestampsHistory are +// properly retrieved by the producer for the correct frames. +// 2) When framesAgo is 0, it is querying for the most recently queued frame. +TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) { + enableFrameTimestamps(); + + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(true, NO_FRAME_INDEX, 0); + mFrames[0].signalRefreshFences(); + addFrameEvents(true, 0, 1); + mFrames[0].signalReleaseFences(); + mFrames[1].signalRefreshFences(); + + // Verify timestamps are correct for frame 1. + uint32_t framesAgo = 1; + resetTimestamps(); + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime, + outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime); + EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); + + // Verify timestamps are correct for frame 2. + framesAgo = 0; + resetTimestamps(); + result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime, + outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(0, outDisplayRetireTime); + EXPECT_EQ(0, outReleaseTime); +} + +// This test verifies the acquire fence recorded by the consumer is not sent +// back to the producer and the producer saves its own fence. +TEST_F(GetFrameTimestampsTest, QueueTimestampsNoSync) { + enableFrameTimestamps(); + mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true); + + const uint32_t framesAgo = 0; + + // Dequeue and queue frame 1. + dequeueAndQueue(0); + + // Verify queue-related timestamps for f1 are available immediately in the + // producer without asking the consumer again, even before signaling the + // acquire fence. + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(0, outAcquireTime); + + // Signal acquire fences. Verify a sync call still isn't necessary. + mFrames[0].signalQueueFences(); + + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + + // Dequeue and queue frame 2. + dequeueAndQueue(1); + + // Verify queue-related timestamps for f2 are available immediately in the + // producer without asking the consumer again, even before signaling the + // acquire fence. + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(0, outAcquireTime); + + // Signal acquire fences. Verify a sync call still isn't necessary. + mFrames[1].signalQueueFences(); + + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); +} + +TEST_F(GetFrameTimestampsTest, ZeroRequestedTimestampsNoSync) { + enableFrameTimestamps(); + mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true); + + // Dequeue and queue frame 1. + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + // Dequeue and queue frame 2. + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(true, NO_FRAME_INDEX, 0); + mFrames[0].signalRefreshFences(); + addFrameEvents(true, 0, 1); + mFrames[0].signalReleaseFences(); + mFrames[1].signalRefreshFences(); + + // Verify a request for no timestamps doesn't result in a sync call. + const uint32_t framesAgo = 0; + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); +} + +// This test verifies that fences can signal and update timestamps producer +// side without an additional sync call to the consumer. +TEST_F(GetFrameTimestampsTest, FencesInProducerNoSync) { + enableFrameTimestamps(); + mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true); + + // Dequeue and queue frame 1. + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + // Dequeue and queue frame 2. + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(true, NO_FRAME_INDEX, 0); + addFrameEvents(true, 0, 1); + + // Verify available timestamps are correct for frame 1, before any + // fence has been signaled. + // Note: A sync call is necessary here since the events triggered by + // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. + uint32_t framesAgo = 1; + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(0, outGpuCompositionDoneTime); + EXPECT_EQ(0, outDisplayPresentTime); + EXPECT_EQ(0, outDisplayRetireTime); + EXPECT_EQ(0, outReleaseTime); + + // Verify available timestamps are correct for frame 1 again, before any + // fence has been signaled. + // This time a sync call should not be necessary. + framesAgo = 1; + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(0, outGpuCompositionDoneTime); + EXPECT_EQ(0, outDisplayPresentTime); + EXPECT_EQ(0, outDisplayRetireTime); + EXPECT_EQ(0, outReleaseTime); + + // Signal the fences for frame 1. + mFrames[0].signalRefreshFences(); + mFrames[0].signalReleaseFences(); + + // Verify all timestamps are available without a sync call. + framesAgo = 1; + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime, + outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime); + EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); +} + +// This test verifies that if the frame wasn't GPU composited but has a refresh +// event a sync call isn't made to get the GPU composite done time since it will +// never exist. +TEST_F(GetFrameTimestampsTest, NoGpuNoSync) { + enableFrameTimestamps(); + mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true); + + const uint32_t framesAgo = 1; + + // Dequeue and queue frame 1. + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + // Dequeue and queue frame 2. + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(false, NO_FRAME_INDEX, 0); + addFrameEvents(false, 0, 1); + + // Verify available timestamps are correct for frame 1, before any + // fence has been signaled. + // Note: A sync call is necessary here since the events triggered by + // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(0, outGpuCompositionDoneTime); + EXPECT_EQ(0, outDisplayPresentTime); + EXPECT_EQ(0, outDisplayRetireTime); + EXPECT_EQ(0, outReleaseTime); + + // Signal the fences for frame 1. + mFrames[0].signalRefreshFences(); + mFrames[0].signalReleaseFences(); + + // Verify all timestamps, except GPU composition, are available without a + // sync call. + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(0, outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime); + EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); +} + +// This test verifies that if the retire/release info can't possibly exist, +// a sync call is not done. +TEST_F(GetFrameTimestampsTest, NoRetireOrReleaseNoSync) { + enableFrameTimestamps(); + mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true); + + // Dequeue and queue frame 1. + dequeueAndQueue(0); + mFrames[0].signalQueueFences(); + + // Dequeue and queue frame 2. + dequeueAndQueue(1); + mFrames[1].signalQueueFences(); + + addFrameEvents(false, NO_FRAME_INDEX, 0); + addFrameEvents(false, 0, 1); + + // Verify available timestamps are correct for frame 1, before any + // fence has been signaled. + // Note: A sync call is necessary here since the events triggered by + // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. + uint32_t framesAgo = 1; + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(0, outGpuCompositionDoneTime); + EXPECT_EQ(0, outDisplayPresentTime); + EXPECT_EQ(0, outDisplayRetireTime); + EXPECT_EQ(0, outReleaseTime); + + mFrames[0].signalRefreshFences(); + mFrames[0].signalReleaseFences(); + mFrames[1].signalRefreshFences(); + + // Verify querying for all timestmaps of f2 does not do a sync call. + // Even though the retire and release times aren't available, a sync call + // should not occur because it's not possible for it to be retired or + // released until another frame is queued. + framesAgo = 0; + resetTimestamps(); + oldCount = mFakeConsumer->mGetFrameTimestampsCount; + result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + &outRequestedPresentTime, &outAcquireTime, &outRefreshStartTime, + &outGpuCompositionDoneTime, &outDisplayPresentTime, + &outDisplayRetireTime, &outReleaseTime); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); + EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outRefreshStartTime); + EXPECT_EQ(0, outGpuCompositionDoneTime); + EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime); + EXPECT_EQ(0, outDisplayRetireTime); + EXPECT_EQ(0, outReleaseTime); +} + +// This test verifies there are no sync calls for present or retire times +// when they aren't supported and that an error is returned. +void GetFrameTimestampsTest::PresentOrRetireUnsupportedNoSyncTest( + bool displayPresentSupported, bool displayRetireSupported) { + + enableFrameTimestamps(); + mSurface->mFakeSurfaceComposer->setSupportedTimestamps( + displayPresentSupported, displayRetireSupported); + + // Dequeue and queue frame 1. + dequeueAndQueue(0); + + // Verify a query for the Present and Retire times do not trigger + // a sync call if they are not supported. + const uint32_t framesAgo = 0; + resetTimestamps(); + int oldCount = mFakeConsumer->mGetFrameTimestampsCount; + int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo, + nullptr, nullptr, nullptr, nullptr, + displayPresentSupported ? nullptr : &outDisplayPresentTime, + displayRetireSupported ? nullptr : &outDisplayRetireTime, + nullptr); + EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(BAD_VALUE, result); + EXPECT_EQ(-1, outDisplayRetireTime); + EXPECT_EQ(-1, outDisplayPresentTime); +} + +TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) { + PresentOrRetireUnsupportedNoSyncTest(false, true); +} + +TEST_F(GetFrameTimestampsTest, RetireUnsupportedNoSync) { + PresentOrRetireUnsupportedNoSyncTest(true, false); +} + } diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp index c0245ebb89..edcec99a23 100644 --- a/libs/ui/FenceTime.cpp +++ b/libs/ui/FenceTime.cpp @@ -130,6 +130,14 @@ nsecs_t FenceTime::getSignalTime() { // Make the system call without the lock held. signalTime = fence->getSignalTime(); + // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests + // use invalid underlying Fences without real file descriptors. + if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) { + if (signalTime == Fence::SIGNAL_TIME_INVALID) { + signalTime = Fence::SIGNAL_TIME_PENDING; + } + } + // Make the signal time visible to everyone if it is no longer pending // and remove the class' reference to the fence. if (signalTime != Fence::SIGNAL_TIME_PENDING) { @@ -163,10 +171,28 @@ FenceTime::Snapshot FenceTime::getSnapshot() const { return Snapshot(mFence); } +// For tests only. If forceValidForTest is true, then getSignalTime will +// never return SIGNAL_TIME_INVALID and isValid will always return true. +FenceTime::FenceTime(const sp<Fence>& fence, bool forceValidForTest) + : mState(forceValidForTest ? + State::FORCED_VALID_FOR_TEST : State::INVALID), + mFence(fence), + mSignalTime(mState == State::INVALID ? + Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { +} + +void FenceTime::signalForTest(nsecs_t signalTime) { + // To be realistic, this should really set a hidden value that + // gets picked up in the next call to getSignalTime, but this should + // be good enough. + std::lock_guard<std::mutex> lock(mMutex); + mFence.clear(); + mSignalTime.store(signalTime, std::memory_order_relaxed); +} + // ============================================================================ // FenceTime::Snapshot // ============================================================================ - FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence) : state(State::FENCE), fence(srcFence) { } @@ -279,4 +305,60 @@ void FenceTimeline::updateSignalTimes() { } } +// ============================================================================ +// FenceToFenceTimeMap +// ============================================================================ +std::shared_ptr<FenceTime> FenceToFenceTimeMap::createFenceTimeForTest( + const sp<Fence>& fence) { + std::lock_guard<std::mutex> lock(mMutex); + // Always garbage collecting isn't efficient, but this is only for testing. + garbageCollectLocked(); + std::shared_ptr<FenceTime> fenceTime(new FenceTime(fence, true)); + mMap[fence.get()].push_back(fenceTime); + return fenceTime; +} + +void FenceToFenceTimeMap::signalAllForTest( + const sp<Fence>& fence, nsecs_t signalTime) { + bool signaled = false; + + std::lock_guard<std::mutex> lock(mMutex); + auto it = mMap.find(fence.get()); + if (it != mMap.end()) { + for (auto& weakFenceTime : it->second) { + std::shared_ptr<FenceTime> fenceTime = weakFenceTime.lock(); + if (!fenceTime) { + continue; + } + ALOGE_IF(!fenceTime->isValid(), + "FenceToFenceTimeMap::signalAllForTest: " + "Signaling invalid fence."); + fenceTime->signalForTest(signalTime); + signaled = true; + } + } + + if (!signaled) { + ALOGE("FenceToFenceTimeMap::signalAllForTest: Nothing to signal."); + } +} + +void FenceToFenceTimeMap::garbageCollectLocked() { + for (auto& it : mMap) { + // Erase all expired weak pointers from the vector. + auto& vect = it.second; + vect.erase( + std::remove_if(vect.begin(), vect.end(), + [](const std::weak_ptr<FenceTime>& ft) { + return ft.expired(); + }), + vect.end()); + + // Also erase the map entry if the vector is now empty. + if (vect.empty()) { + mMap.erase(it.first); + } + } +} + } // namespace android |