/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MockConsumer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { using namespace std::chrono_literals; // retrieve wide-color and hdr settings from configstore using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; using aidl::android::hardware::graphics::common::DisplayDecorationSupport; using gui::IDisplayEventConnection; using gui::IRegionSamplingListener; using ui::ColorMode; using Transaction = SurfaceComposerClient::Transaction; static bool hasWideColorDisplay = android::sysprop::has_wide_color_display(false); static bool hasHdrDisplay = android::sysprop::has_HDR_display(false); class FakeSurfaceComposer; class FakeProducerFrameEventHistory; static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits::max(); class FakeSurfaceListener : public SurfaceListener { public: FakeSurfaceListener(bool enableReleasedCb = false) : mEnableReleaseCb(enableReleasedCb), mBuffersReleased(0) {} virtual ~FakeSurfaceListener() = default; virtual void onBufferReleased() { mBuffersReleased++; } virtual bool needsReleaseNotify() { return mEnableReleaseCb; } virtual void onBuffersDiscarded(const std::vector>& buffers) { mDiscardedBuffers.insert(mDiscardedBuffers.end(), buffers.begin(), buffers.end()); } int getReleaseNotifyCount() const { return mBuffersReleased; } const std::vector>& getDiscardedBuffers() const { return mDiscardedBuffers; } private: // No need to use lock given the test triggers the listener in the same // thread context. bool mEnableReleaseCb; int32_t mBuffersReleased; std::vector> mDiscardedBuffers; }; class SurfaceTest : public ::testing::Test { protected: SurfaceTest() { ProcessState::self()->startThreadPool(); } virtual void SetUp() { mComposerClient = new SurfaceComposerClient; ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); // TODO(brianderson): The following sometimes fails and is a source of // test flakiness. mSurfaceControl = mComposerClient->createSurface( String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0); SurfaceComposerClient::Transaction().apply(true); ASSERT_TRUE(mSurfaceControl != nullptr); ASSERT_TRUE(mSurfaceControl->isValid()); Transaction t; ASSERT_EQ(NO_ERROR, t.setLayer(mSurfaceControl, 0x7fffffff).show(mSurfaceControl).apply()); mSurface = mSurfaceControl->getSurface(); ASSERT_TRUE(mSurface != nullptr); } virtual void TearDown() { mComposerClient->dispose(); } void testSurfaceListener(bool hasSurfaceListener, bool enableReleasedCb, int32_t extraDiscardedBuffers) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); sp listener; if (hasSurfaceListener) { listener = new FakeSurfaceListener(enableReleasedCb); } ASSERT_EQ(OK, surface->connect( NATIVE_WINDOW_API_CPU, /*reportBufferRemoval*/true, /*listener*/listener)); const int BUFFER_COUNT = 4 + extraDiscardedBuffers; ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); ANativeWindowBuffer* buffers[BUFFER_COUNT]; // Dequeue first to allocate a number of buffers for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffers[i])); } for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], -1)); } ANativeWindowBuffer* buffer; // Fill BUFFER_COUNT-1 buffers for (int i = 0; i < BUFFER_COUNT-1; i++) { ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer)); ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, -1)); } // Dequeue 1 buffer ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer)); // Acquire and free 1+extraDiscardedBuffers buffer, check onBufferReleased is called. std::vector releasedItems; releasedItems.resize(1+extraDiscardedBuffers); for (int i = 0; i < releasedItems.size(); i++) { ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0)); ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot, releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); } int32_t expectedReleaseCb = (enableReleasedCb ? releasedItems.size() : 0); if (hasSurfaceListener) { ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); } // Acquire 1 buffer, leaving 1+extraDiscardedBuffers filled buffer in queue BufferItem item; ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&item, 0)); // Discard free buffers ASSERT_EQ(NO_ERROR, consumer->discardFreeBuffers()); if (hasSurfaceListener) { ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); // Check onBufferDiscarded is called with correct buffer auto discardedBuffers = listener->getDiscardedBuffers(); ASSERT_EQ(discardedBuffers.size(), releasedItems.size()); for (int i = 0; i < releasedItems.size(); i++) { ASSERT_EQ(discardedBuffers[i], releasedItems[i].mGraphicBuffer); } ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); } // Disconnect the surface ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } static status_t captureDisplay(DisplayCaptureArgs& captureArgs, ScreenCaptureResults& captureResults) { const auto sf = ComposerServiceAIDL::getComposerService(); SurfaceComposerClient::Transaction().apply(true); const sp captureListener = new SyncScreenCaptureListener(); binder::Status status = sf->captureDisplay(captureArgs, captureListener); if (status.transactionError() != NO_ERROR) { return status.transactionError(); } captureResults = captureListener->waitForResults(); return captureResults.result; } sp mSurface; sp mComposerClient; sp mSurfaceControl; }; TEST_F(SurfaceTest, CreateSurfaceReturnsErrorBadClient) { mComposerClient->dispose(); ASSERT_EQ(NO_INIT, mComposerClient->initCheck()); sp sc; status_t err = mComposerClient->createSurfaceChecked( String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, &sc, 0); ASSERT_EQ(NO_INIT, err); } TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenVisible) { sp anw(mSurface); int result = -123; int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(1, result); } TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) { mSurfaceControl.clear(); // Wait for the async clean-up to complete. std::this_thread::sleep_for(50ms); sp anw(mSurface); int result = -123; int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(1, result); } // This test probably doesn't belong here. TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) { sp anw(mSurface); // Verify the screenshot works with no protected buffers. const sp display = ComposerServiceAIDL::getInstance().getInternalDisplayToken(); ASSERT_FALSE(display == nullptr); DisplayCaptureArgs captureArgs; captureArgs.displayToken = display; captureArgs.width = 64; captureArgs.height = 64; ScreenCaptureResults captureResults; ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults)); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); // Set the PROTECTED usage bit and verify that the screenshot fails. Note // that we need to dequeue a buffer in order for it to actually get // allocated in SurfaceFlinger. ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); ANativeWindowBuffer* buf = nullptr; status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf); if (err) { // we could fail if GRALLOC_USAGE_PROTECTED is not supported. // that's okay as long as this is the reason for the failure. // try again without the GRALLOC_USAGE_PROTECTED bit. ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0)); ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), &buf)); return; } ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf, -1)); for (int i = 0; i < 4; i++) { // Loop to make sure SurfaceFlinger has retired a protected buffer. ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { sp anw(mSurface); int result = -123; int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(NATIVE_WINDOW_SURFACE, result); } TEST_F(SurfaceTest, LayerCountIsOne) { sp anw(mSurface); int result = -123; int err = anw->query(anw.get(), NATIVE_WINDOW_LAYER_COUNT, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(1, result); } TEST_F(SurfaceTest, QueryConsumerUsage) { const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp c = new BufferItemConsumer(consumer, TEST_USAGE_FLAGS); sp s = new Surface(producer); sp anw(s); int flags = -1; int err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &flags); ASSERT_EQ(NO_ERROR, err); ASSERT_EQ(TEST_USAGE_FLAGS, flags); } TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB; sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp cpuConsumer = new CpuConsumer(consumer, 1); cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE); sp s = new Surface(producer); sp anw(s); android_dataspace dataSpace; int err = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, reinterpret_cast(&dataSpace)); ASSERT_EQ(NO_ERROR, err); ASSERT_EQ(TEST_DATASPACE, dataSpace); } TEST_F(SurfaceTest, SettingGenerationNumber) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp cpuConsumer = new CpuConsumer(consumer, 1); sp surface = new Surface(producer); sp window(surface); // Allocate a buffer with a generation number of 0 ANativeWindowBuffer* buffer; int fenceFd; ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fenceFd)); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fenceFd)); // Detach the buffer and check its generation number sp graphicBuffer; sp fence; ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&graphicBuffer, &fence)); ASSERT_EQ(0U, graphicBuffer->getGenerationNumber()); ASSERT_EQ(NO_ERROR, surface->setGenerationNumber(1)); buffer = static_cast(graphicBuffer.get()); // This should change the generation number of the GraphicBuffer ASSERT_EQ(NO_ERROR, surface->attachBuffer(buffer)); // Check that the new generation number sticks with the buffer ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, -1)); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fenceFd)); graphicBuffer = static_cast(buffer); ASSERT_EQ(1U, graphicBuffer->getGenerationNumber()); } TEST_F(SurfaceTest, GetConsumerName) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); EXPECT_STREQ("TestConsumer", surface->getConsumerName().string()); } TEST_F(SurfaceTest, GetWideColorSupport) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); bool supported; surface->getWideColorSupport(&supported); // NOTE: This test assumes that device that supports // wide-color (as indicated by BoardConfig) must also // have a wide-color primary display. // That assumption allows this test to cover devices // that advertised a wide-color color mode without // actually supporting wide-color to pass this test // as well as the case of a device that does support // wide-color (via BoardConfig) and has a wide-color // primary display. // NOT covered at this time is a device that supports // wide color in the BoardConfig but does not support // a wide-color color mode on the primary display. ASSERT_EQ(hasWideColorDisplay, supported); } TEST_F(SurfaceTest, GetHdrSupport) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); bool supported; status_t result = surface->getHdrSupport(&supported); ASSERT_EQ(NO_ERROR, result); // NOTE: This is not a CTS test. // This test verifies that when the BoardConfig TARGET_HAS_HDR_DISPLAY // is TRUE, getHdrSupport is also true. // TODO: Add check for an HDR color mode on the primary display. ASSERT_EQ(hasHdrDisplay, supported); } TEST_F(SurfaceTest, SetHdrMetadata) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); bool supported; status_t result = surface->getHdrSupport(&supported); ASSERT_EQ(NO_ERROR, result); if (!hasHdrDisplay || !supported) { return; } const android_smpte2086_metadata smpte2086 = { {0.680, 0.320}, {0.265, 0.690}, {0.150, 0.060}, {0.3127, 0.3290}, 100.0, 0.1, }; const android_cta861_3_metadata cta861_3 = { 78.0, 62.0, }; std::vector hdr10plus; hdr10plus.push_back(0xff); int error = native_window_set_buffers_smpte2086_metadata(window.get(), &smpte2086); ASSERT_EQ(error, NO_ERROR); error = native_window_set_buffers_cta861_3_metadata(window.get(), &cta861_3); ASSERT_EQ(error, NO_ERROR); error = native_window_set_buffers_hdr10_plus_metadata(window.get(), hdr10plus.size(), hdr10plus.data()); ASSERT_EQ(error, NO_ERROR); } TEST_F(SurfaceTest, DynamicSetBufferCount) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU)); native_window_set_buffer_count(window.get(), 4); int fence; ANativeWindowBuffer* buffer; ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); native_window_set_buffer_count(window.get(), 3); ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); native_window_set_buffer_count(window.get(), 2); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); } TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); sp listener = new StubProducerListener(); ASSERT_EQ(OK, surface->connect( NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/true)); const int BUFFER_COUNT = 4; ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); sp detachedBuffer; sp outFence; int fences[BUFFER_COUNT]; ANativeWindowBuffer* buffers[BUFFER_COUNT]; // Allocate buffers because detachNextBuffer requires allocated buffers for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i])); } for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i])); } // Test detached buffer is correctly reported ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); std::vector> removedBuffers; ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); ASSERT_EQ(1u, removedBuffers.size()); ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle); // Test the list is flushed one getAndFlushRemovedBuffers returns ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); ASSERT_EQ(0u, removedBuffers.size()); // Test removed buffer list is cleanup after next dequeueBuffer call ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[0], &fences[0])); ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); ASSERT_EQ(0u, removedBuffers.size()); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[0], fences[0])); // Test removed buffer list is cleanup after next detachNextBuffer call ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); ASSERT_EQ(1u, removedBuffers.size()); ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle); // Re-allocate buffers since all buffers are detached up to now for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i])); } for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i])); } ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); ASSERT_EQ(NO_ERROR, surface->attachBuffer(detachedBuffer.get())); ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); // Depends on which slot GraphicBufferProducer impl pick, the attach call might // get 0 or 1 buffer removed. ASSERT_LE(removedBuffers.size(), 1u); } TEST_F(SurfaceTest, SurfaceListenerTest) { // Test discarding 1 free buffers with no listener testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/0); // Test discarding 2 free buffers with no listener testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/1); // Test discarding 1 free buffers with a listener, disabling onBufferReleased testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/0); // Test discarding 2 free buffers with a listener, disabling onBufferReleased testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/1); // Test discarding 1 free buffers with a listener, enabling onBufferReleased testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/0); // Test discarding 3 free buffers with a listener, enabling onBufferReleased testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/2); } TEST_F(SurfaceTest, TestGetLastDequeueStartTime) { sp anw(mSurface); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer* buffer = nullptr; int32_t fenceFd = -1; nsecs_t before = systemTime(CLOCK_MONOTONIC); anw->dequeueBuffer(anw.get(), &buffer, &fenceFd); nsecs_t after = systemTime(CLOCK_MONOTONIC); nsecs_t lastDequeueTime = ANativeWindow_getLastDequeueStartTime(anw.get()); ASSERT_LE(before, lastDequeueTime); ASSERT_GE(after, lastDequeueTime); } 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 setSupportsPresent(bool supportsPresent) { mSupportsPresent = supportsPresent; } sp createConnection() override { return nullptr; } sp createDisplayEventConnection( ISurfaceComposer::VsyncSource, ISurfaceComposer::EventRegistrationFlags) override { return nullptr; } status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/, const Vector& /*state*/, const Vector& /*displays*/, uint32_t /*flags*/, const sp& /*applyToken*/, const InputWindowCommands& /*inputWindowCommands*/, int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, const client_cache_t& /*cachedBuffer*/, bool /*hasListenerCallbacks*/, const std::vector& /*listenerCallbacks*/, uint64_t /*transactionId*/) override { return NO_ERROR; } void bootFinished() override {} bool authenticateSurfaceTexture( const sp& /*surface*/) const override { return false; } status_t getSupportedFrameTimestamps(std::vector* outSupported) const override { *outSupported = { FrameEvent::REQUESTED_PRESENT, FrameEvent::ACQUIRE, FrameEvent::LATCH, FrameEvent::FIRST_REFRESH_START, FrameEvent::LAST_REFRESH_START, FrameEvent::GPU_COMPOSITION_DONE, FrameEvent::DEQUEUE_READY, FrameEvent::RELEASE }; if (mSupportsPresent) { outSupported->push_back( FrameEvent::DISPLAY_PRESENT); } return NO_ERROR; } status_t getStaticDisplayInfo(const sp& /*display*/, ui::StaticDisplayInfo*) override { return NO_ERROR; } status_t getDynamicDisplayInfo(const sp& /*display*/, ui::DynamicDisplayInfo*) override { return NO_ERROR; } status_t getDisplayNativePrimaries(const sp& /*display*/, ui::DisplayPrimaries& /*primaries*/) override { return NO_ERROR; } status_t setActiveColorMode(const sp& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } status_t setBootDisplayMode(const sp& /*display*/, ui::DisplayModeId /*id*/) override { return NO_ERROR; } status_t clearAnimationFrameStats() override { return NO_ERROR; } status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { return NO_ERROR; } status_t overrideHdrTypes(const sp& /*display*/, const std::vector& /*hdrTypes*/) override { return NO_ERROR; } status_t onPullAtom(const int32_t /*atomId*/, std::string* /*outData*/, bool* /*success*/) override { return NO_ERROR; } status_t enableVSyncInjections(bool /*enable*/) override { return NO_ERROR; } status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; } status_t getLayerDebugInfo(std::vector* /*layers*/) override { return NO_ERROR; } status_t getCompositionPreference( ui::Dataspace* /*outDefaultDataspace*/, ui::PixelFormat* /*outDefaultPixelFormat*/, ui::Dataspace* /*outWideColorGamutDataspace*/, ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override { return NO_ERROR; } status_t getDisplayedContentSamplingAttributes(const sp& /*display*/, ui::PixelFormat* /*outFormat*/, ui::Dataspace* /*outDataspace*/, uint8_t* /*outComponentMask*/) const override { return NO_ERROR; } status_t setDisplayContentSamplingEnabled(const sp& /*display*/, bool /*enable*/, uint8_t /*componentMask*/, uint64_t /*maxFrames*/) override { return NO_ERROR; } status_t getDisplayedContentSample(const sp& /*display*/, uint64_t /*maxFrames*/, uint64_t /*timestamp*/, DisplayedFrameStats* /*outStats*/) const override { return NO_ERROR; } status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; } status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; } status_t addRegionSamplingListener(const Rect& /*samplingArea*/, const sp& /*stopLayerHandle*/, const sp& /*listener*/) override { return NO_ERROR; } status_t removeRegionSamplingListener( const sp& /*listener*/) override { return NO_ERROR; } status_t addFpsListener(int32_t /*taskId*/, const sp& /*listener*/) { return NO_ERROR; } status_t removeFpsListener(const sp& /*listener*/) { return NO_ERROR; } status_t addTunnelModeEnabledListener(const sp& /*listener*/) { return NO_ERROR; } status_t removeTunnelModeEnabledListener( const sp& /*listener*/) { return NO_ERROR; } status_t setDesiredDisplayModeSpecs(const sp& /*displayToken*/, ui::DisplayModeId /*defaultMode*/, bool /*allowGroupSwitching*/, float /*primaryRefreshRateMin*/, float /*primaryRefreshRateMax*/, float /*appRequestRefreshRateMin*/, float /*appRequestRefreshRateMax*/) { return NO_ERROR; } status_t getDesiredDisplayModeSpecs(const sp& /*displayToken*/, ui::DisplayModeId* /*outDefaultMode*/, bool* /*outAllowGroupSwitching*/, float* /*outPrimaryRefreshRateMin*/, float* /*outPrimaryRefreshRateMax*/, float* /*outAppRequestRefreshRateMin*/, float* /*outAppRequestRefreshRateMax*/) override { return NO_ERROR; }; status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/, float /*lightPosY*/, float /*lightPosZ*/, float /*lightRadius*/) override { return NO_ERROR; } status_t getDisplayDecorationSupport( const sp& /*displayToken*/, std::optional* /*outSupport*/) const override { return NO_ERROR; } status_t setFrameRate(const sp& /*surface*/, float /*frameRate*/, int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override { return NO_ERROR; } status_t setFrameTimelineInfo(const sp& /*surface*/, const FrameTimelineInfo& /*frameTimelineInfo*/) override { return NO_ERROR; } status_t addTransactionTraceListener( const sp& /*listener*/) override { return NO_ERROR; } int getGPUContextPriority() override { return 0; }; status_t getMaxAcquiredBufferCount(int* /*buffers*/) const override { return NO_ERROR; } status_t addWindowInfosListener( const sp& /*windowInfosListener*/) const override { return NO_ERROR; } status_t removeWindowInfosListener( const sp& /*windowInfosListener*/) const override { return NO_ERROR; } status_t setOverrideFrameRate(uid_t /*uid*/, float /*frameRate*/) override { return NO_ERROR; } protected: IBinder* onAsBinder() override { return nullptr; } private: bool mSupportsPresent{true}; }; class FakeSurfaceComposerAIDL : public gui::ISurfaceComposer { public: ~FakeSurfaceComposerAIDL() override {} void setSupportsPresent(bool supportsPresent) { mSupportsPresent = supportsPresent; } binder::Status createDisplay(const std::string& /*displayName*/, bool /*secure*/, sp* /*outDisplay*/) override { return binder::Status::ok(); } binder::Status destroyDisplay(const sp& /*display*/) override { return binder::Status::ok(); } binder::Status getPhysicalDisplayIds(std::vector* /*outDisplayIds*/) override { return binder::Status::ok(); } binder::Status getPrimaryPhysicalDisplayId(int64_t* /*outDisplayId*/) override { return binder::Status::ok(); } binder::Status getPhysicalDisplayToken(int64_t /*displayId*/, sp* /*outDisplay*/) override { return binder::Status::ok(); } binder::Status setPowerMode(const sp& /*display*/, int /*mode*/) override { return binder::Status::ok(); } binder::Status getDisplayStats(const sp& /*display*/, gui::DisplayStatInfo* /*outStatInfo*/) override { return binder::Status::ok(); } binder::Status getDisplayState(const sp& /*display*/, gui::DisplayState* /*outState*/) override { return binder::Status::ok(); } binder::Status clearBootDisplayMode(const sp& /*display*/) override { return binder::Status::ok(); } binder::Status getBootDisplayModeSupport(bool* /*outMode*/) override { return binder::Status::ok(); } binder::Status setAutoLowLatencyMode(const sp& /*display*/, bool /*on*/) override { return binder::Status::ok(); } binder::Status setGameContentType(const sp& /*display*/, bool /*on*/) override { return binder::Status::ok(); } binder::Status captureDisplay(const DisplayCaptureArgs&, const sp&) override { return binder::Status::ok(); } binder::Status captureDisplayById(int64_t, const sp&) override { return binder::Status::ok(); } binder::Status captureLayers(const LayerCaptureArgs&, const sp&) override { return binder::Status::ok(); } binder::Status isWideColorDisplay(const sp& /*token*/, bool* /*outIsWideColorDisplay*/) override { return binder::Status::ok(); } binder::Status getDisplayBrightnessSupport(const sp& /*displayToken*/, bool* /*outSupport*/) override { return binder::Status::ok(); } binder::Status setDisplayBrightness(const sp& /*displayToken*/, const gui::DisplayBrightness& /*brightness*/) override { return binder::Status::ok(); } binder::Status addHdrLayerInfoListener( const sp& /*displayToken*/, const sp& /*listener*/) override { return binder::Status::ok(); } binder::Status removeHdrLayerInfoListener( const sp& /*displayToken*/, const sp& /*listener*/) override { return binder::Status::ok(); } binder::Status notifyPowerBoost(int /*boostId*/) override { return binder::Status::ok(); } protected: IBinder* onAsBinder() override { return nullptr; } private: bool mSupportsPresent{true}; }; class FakeProducerFrameEventHistory : public ProducerFrameEventHistory { public: explicit FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap) : mFenceMap(fenceMap) {} ~FakeProducerFrameEventHistory() {} void updateAcquireFence(uint64_t frameNumber, std::shared_ptr&& 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(mAcquireFenceOverride)); } void setAcquireFenceOverride( const std::shared_ptr& acquireFenceOverride, const std::shared_ptr& consumerAcquireFence) { mAcquireFenceOverride = acquireFenceOverride; mConsumerAcquireFence = consumerAcquireFence; } protected: std::shared_ptr createFenceTime(const sp& fence) const override { return mFenceMap->createFenceTimeForTest(fence); } FenceToFenceTimeMap* mFenceMap{nullptr}; std::shared_ptr mAcquireFenceOverride{FenceTime::NO_FENCE}; std::shared_ptr mConsumerAcquireFence{FenceTime::NO_FENCE}; }; class TestSurface : public Surface { public: TestSurface(const sp& bufferProducer, FenceToFenceTimeMap* fenceMap) : Surface(bufferProducer), mFakeSurfaceComposer(new FakeSurfaceComposer) { mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap); mFrameEventHistory.reset(mFakeFrameEventHistory); } ~TestSurface() override {} sp composerService() const override { return mFakeSurfaceComposer; } nsecs_t now() const override { return mNow; } void setNow(nsecs_t now) { mNow = now; } public: sp mFakeSurfaceComposer; nsecs_t mNow = 0; // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory, // but this raw pointer gives access to test functionality. FakeProducerFrameEventHistory* mFakeFrameEventHistory; }; class GetFrameTimestampsTest : public ::testing::Test { protected: struct FenceAndFenceTime { explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap) : mFence(new Fence), mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {} sp mFence { nullptr }; std::shared_ptr mFenceTime { nullptr }; }; struct RefreshEvents { RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart) : mFenceMap(fenceMap), kCompositorTiming( {refreshStart, refreshStart + 1, refreshStart + 2 }), kStartTime(refreshStart + 3), kGpuCompositionDoneTime(refreshStart + 4), kPresentTime(refreshStart + 5) {} void signalPostCompositeFences() { mFenceMap.signalAllForTest( mGpuCompositionDone.mFence, kGpuCompositionDoneTime); mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime); } FenceToFenceTimeMap& mFenceMap; FenceAndFenceTime mGpuCompositionDone { mFenceMap }; FenceAndFenceTime mPresent { mFenceMap }; const CompositorTiming kCompositorTiming; 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), kDequeueReadyTime(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(mRelease.mFence, kReleaseTime); } FenceToFenceTimeMap& mFenceMap; FenceAndFenceTime mAcquireConsumer { mFenceMap }; FenceAndFenceTime mAcquireProducer { 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 kDequeueReadyTime; const nsecs_t kReleaseTime; RefreshEvents mRefreshes[3]; }; GetFrameTimestampsTest() {} virtual void 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 disableFrameTimestamps() { mFakeConsumer->mGetFrameTimestampsEnabled = false; native_window_enable_frame_timestamps(mWindow.get(), 0); mFrameTimestampsEnabled = false; } void enableFrameTimestamps() { mFakeConsumer->mGetFrameTimestampsEnabled = true; native_window_enable_frame_timestamps(mWindow.get(), 1); mFrameTimestampsEnabled = true; } int getAllFrameTimestamps(uint64_t frameId) { return native_window_get_frame_timestamps(mWindow.get(), frameId, &outRequestedPresentTime, &outAcquireTime, &outLatchTime, &outFirstRefreshStartTime, &outLastRefreshStartTime, &outGpuCompositionDoneTime, &outDisplayPresentTime, &outDequeueReadyTime, &outReleaseTime); } void resetTimestamps() { outRequestedPresentTime = -1; outAcquireTime = -1; outLatchTime = -1; outFirstRefreshStartTime = -1; outLastRefreshStartTime = -1; outGpuCompositionDoneTime = -1; outDisplayPresentTime = -1; outDequeueReadyTime = -1; outReleaseTime = -1; } uint64_t getNextFrameId() { uint64_t frameId = -1; int status = native_window_get_next_frame_id(mWindow.get(), &frameId); EXPECT_EQ(status, NO_ERROR); return frameId; } 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 == NO_FRAME_INDEX) ? 0 : iOldFrame + 1; uint64_t nNewFrame = iNewFrame + 1; // Latch, Composite, 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 gpuDoneFenceTime = FenceTime::NO_FENCE; // Composite the previous frame one more time, which helps verify // LastRefresh is updated properly. if (oldFrame != nullptr) { mCfeh->addPreComposition(nOldFrame, oldFrame->mRefreshes[2].kStartTime); gpuDoneFenceTime = gpuComposited ? oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime, oldFrame->mRefreshes[2].mPresent.mFenceTime, oldFrame->mRefreshes[2].kCompositorTiming); } // Latch the new frame. 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, oldFrame->kDequeueReadyTime, std::shared_ptr(oldFrame->mRelease.mFenceTime)); } mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, newFrame->mRefreshes[0].mPresent.mFenceTime, newFrame->mRefreshes[0].kCompositorTiming); 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, newFrame->mRefreshes[1].kCompositorTiming); } sp mProducer; sp mConsumer; sp mFakeConsumer; ConsumerFrameEventHistory* mCfeh; sp mSurface; sp mWindow; FenceToFenceTimeMap mFenceMap; bool mFrameTimestampsEnabled = false; int64_t outRequestedPresentTime = -1; int64_t outAcquireTime = -1; int64_t outLatchTime = -1; int64_t outFirstRefreshStartTime = -1; int64_t outLastRefreshStartTime = -1; int64_t outGpuCompositionDoneTime = -1; int64_t outDisplayPresentTime = -1; int64_t outDequeueReadyTime = -1; int64_t outReleaseTime = -1; FrameEvents mFrames[3] { { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } }; }; // 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); const uint64_t fId = getNextFrameId(); // 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. int result = getAllFrameTimestamps(fId); EXPECT_EQ(INVALID_OPERATION, result); EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); // Verify compositor timing query fails. nsecs_t compositeDeadline = 0; nsecs_t compositeInterval = 0; nsecs_t compositeToPresentLatency = 0; result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(INVALID_OPERATION, result); } // This test verifies that the frame timestamps are retrieved if explicitly // enabled via native_window_enable_frame_timestamps. TEST_F(GetFrameTimestampsTest, EnabledSimple) { CompositorTiming initialCompositorTiming { 1000000000, // 1s deadline 16666667, // 16ms interval 50000000, // 50ms present latency }; mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); // Verify the compositor timing query gets the initial compositor values // after timststamps are enabled; even before the first frame is queued // or dequeued. nsecs_t compositeDeadline = 0; nsecs_t compositeInterval = 0; nsecs_t compositeToPresentLatency = 0; mSurface->setNow(initialCompositorTiming.deadline - 1); int result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); EXPECT_EQ(initialCompositorTiming.presentLatency, compositeToPresentLatency); int fence; ANativeWindowBuffer* buffer; EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount); const uint64_t fId1 = getNextFrameId(); // Verify getFrameTimestamps is piggybacked on dequeue. ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(2, 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(3, 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. result = getAllFrameTimestamps(fId1); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount); } TEST_F(GetFrameTimestampsTest, QueryPresentSupported) { bool displayPresentSupported = true; mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported); // Verify supported bits are forwarded. int supportsPresent = -1; mWindow.get()->query(mWindow.get(), NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent); EXPECT_EQ(displayPresentSupported, supportsPresent); } TEST_F(GetFrameTimestampsTest, QueryPresentNotSupported) { bool displayPresentSupported = false; mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported); // Verify supported bits are forwarded. int supportsPresent = -1; mWindow.get()->query(mWindow.get(), NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent); EXPECT_EQ(displayPresentSupported, supportsPresent); } TEST_F(GetFrameTimestampsTest, SnapToNextTickBasic) { nsecs_t phase = 4000; nsecs_t interval = 1000; // Timestamp in previous interval. nsecs_t timestamp = 3500; EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp in next interval. timestamp = 4500; EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp multiple intervals before. timestamp = 2500; EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp multiple intervals after. timestamp = 6500; EXPECT_EQ(7000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp on previous interval. timestamp = 3000; EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp on next interval. timestamp = 5000; EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp equal to phase. timestamp = 4000; EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); } // int(big_timestamp / interval) < 0, which can cause a crash or invalid result // if the number of intervals elapsed is internally stored in an int. TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) { nsecs_t phase = 0; nsecs_t interval = 4000; nsecs_t big_timestamp = 8635916564000; int32_t intervals = big_timestamp / interval; EXPECT_LT(intervals, 0); EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick( big_timestamp, phase, interval)); EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick( big_timestamp, big_timestamp, interval)); } // This verifies the compositor timing is updated by refresh events // and piggy backed on a queue, dequeue, and enabling of timestamps.. TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) { CompositorTiming initialCompositorTiming { 1000000000, // 1s deadline 16666667, // 16ms interval 50000000, // 50ms present latency }; mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); // We get the initial values before any frames are submitted. nsecs_t compositeDeadline = 0; nsecs_t compositeInterval = 0; nsecs_t compositeToPresentLatency = 0; mSurface->setNow(initialCompositorTiming.deadline - 1); int result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); EXPECT_EQ(initialCompositorTiming.presentLatency, compositeToPresentLatency); dequeueAndQueue(0); addFrameEvents(true, NO_FRAME_INDEX, 0); // Still get the initial values because the frame events for frame 0 // didn't get a chance to piggyback on a queue or dequeue yet. result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); EXPECT_EQ(initialCompositorTiming.presentLatency, compositeToPresentLatency); dequeueAndQueue(1); addFrameEvents(true, 0, 1); // Now expect the composite values associated with frame 1. mSurface->setNow(mFrames[0].mRefreshes[1].kCompositorTiming.deadline); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.interval, compositeInterval); EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.presentLatency, compositeToPresentLatency); dequeueAndQueue(2); addFrameEvents(true, 1, 2); // Now expect the composite values associated with frame 2. mSurface->setNow(mFrames[1].mRefreshes[1].kCompositorTiming.deadline); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.interval, compositeInterval); EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.presentLatency, compositeToPresentLatency); // Re-enabling frame timestamps should get the latest values. disableFrameTimestamps(); enableFrameTimestamps(); // Now expect the composite values associated with frame 3. mSurface->setNow(mFrames[2].mRefreshes[1].kCompositorTiming.deadline); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.interval, compositeInterval); EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.presentLatency, compositeToPresentLatency); } // This verifies the compositor deadline properly snaps to the the next // deadline based on the current time. TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) { CompositorTiming initialCompositorTiming { 1000000000, // 1s deadline 16666667, // 16ms interval 50000000, // 50ms present latency }; mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); nsecs_t compositeDeadline = 0; nsecs_t compositeInterval = 0; nsecs_t compositeToPresentLatency = 0; // A "now" just before the deadline snaps to the deadline. mSurface->setNow(initialCompositorTiming.deadline - 1); int result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); nsecs_t expectedDeadline = initialCompositorTiming.deadline; EXPECT_EQ(expectedDeadline, compositeDeadline); dequeueAndQueue(0); addFrameEvents(true, NO_FRAME_INDEX, 0); // A "now" just after the deadline snaps properly. mSurface->setNow(initialCompositorTiming.deadline + 1); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); expectedDeadline = initialCompositorTiming.deadline +initialCompositorTiming.interval; EXPECT_EQ(expectedDeadline, compositeDeadline); dequeueAndQueue(1); addFrameEvents(true, 0, 1); // A "now" just after the next interval snaps properly. mSurface->setNow( mFrames[0].mRefreshes[1].kCompositorTiming.deadline + mFrames[0].mRefreshes[1].kCompositorTiming.interval + 1); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); expectedDeadline = mFrames[0].mRefreshes[1].kCompositorTiming.deadline + mFrames[0].mRefreshes[1].kCompositorTiming.interval * 2; EXPECT_EQ(expectedDeadline, compositeDeadline); dequeueAndQueue(2); addFrameEvents(true, 1, 2); // A "now" over 1 interval before the deadline snaps properly. mSurface->setNow( mFrames[1].mRefreshes[1].kCompositorTiming.deadline - mFrames[1].mRefreshes[1].kCompositorTiming.interval - 1); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); expectedDeadline = mFrames[1].mRefreshes[1].kCompositorTiming.deadline - mFrames[1].mRefreshes[1].kCompositorTiming.interval; EXPECT_EQ(expectedDeadline, compositeDeadline); // Re-enabling frame timestamps should get the latest values. disableFrameTimestamps(); enableFrameTimestamps(); // A "now" over 2 intervals before the deadline snaps properly. mSurface->setNow( mFrames[2].mRefreshes[1].kCompositorTiming.deadline - mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2 - 1); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); expectedDeadline = mFrames[2].mRefreshes[1].kCompositorTiming.deadline - mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2; EXPECT_EQ(expectedDeadline, compositeDeadline); } // This verifies the timestamps recorded in the consumer's // FrameTimestampsHistory are properly retrieved by the producer for the // correct frames. TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) { enableFrameTimestamps(); const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); mFrames[0].signalQueueFences(); const uint64_t fId2 = getNextFrameId(); 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. resetTimestamps(); int result = getAllFrameTimestamps(fId1); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); // Verify timestamps are correct for frame 2. resetTimestamps(); result = getAllFrameTimestamps(fId2); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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(); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); 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(), fId1, &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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(), fId1, &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, 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. const uint64_t fId2 = getNextFrameId(); 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(), fId2, &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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(), fId2, &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, 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(); // Dequeue and queue frame 1. dequeueAndQueue(0); mFrames[0].signalQueueFences(); // Dequeue and queue frame 2. const uint64_t fId2 = getNextFrameId(); 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. int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = native_window_get_frame_timestamps(mWindow.get(), fId2, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(NO_ERROR, result); 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(); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); 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. resetTimestamps(); int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = getAllFrameTimestamps(fId1); 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].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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. resetTimestamps(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = getAllFrameTimestamps(fId1); 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].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); // Signal the fences for frame 1. mFrames[0].signalRefreshFences(); mFrames[0].signalReleaseFences(); // Verify all timestamps are available without a sync call. resetTimestamps(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = getAllFrameTimestamps(fId1); 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].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); 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(); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); 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 = getAllFrameTimestamps(fId1); 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].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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 = getAllFrameTimestamps(fId1); 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].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); } // This test verifies that if the certain timestamps can't possibly exist for // the most recent frame, then a sync call is not done. TEST_F(GetFrameTimestampsTest, NoReleaseNoSync) { enableFrameTimestamps(); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); mFrames[0].signalQueueFences(); // Dequeue and queue frame 2. const uint64_t fId2 = getNextFrameId(); 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 = getAllFrameTimestamps(fId1); 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].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, 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 lastRefresh, dequeueReady, and release times aren't // available, a sync call should not occur because it's not possible for f2 // to encounter the final value for those events until another frame is // queued. resetTimestamps(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = getAllFrameTimestamps(fId2); 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].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); } // This test verifies there are no sync calls for present times // when they aren't supported and that an error is returned. TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) { enableFrameTimestamps(); mSurface->mFakeSurfaceComposer->setSupportsPresent(false); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); // Verify a query for the Present times do not trigger a sync call if they // are not supported. resetTimestamps(); int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = native_window_get_frame_timestamps(mWindow.get(), fId1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &outDisplayPresentTime, nullptr, nullptr); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(BAD_VALUE, result); EXPECT_EQ(-1, outDisplayPresentTime); } TEST_F(SurfaceTest, DequeueWithConsumerDrivenSize) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); consumer->setDefaultBufferSize(10, 10); sp surface = new Surface(producer); sp window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); native_window_set_buffers_dimensions(window.get(), 0, 0); int fence; ANativeWindowBuffer* buffer; // Buffer size is driven by the consumer ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); EXPECT_EQ(10, buffer->width); EXPECT_EQ(10, buffer->height); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); // Buffer size is driven by the consumer consumer->setDefaultBufferSize(10, 20); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); EXPECT_EQ(10, buffer->width); EXPECT_EQ(20, buffer->height); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); // Transform hint isn't synced to producer before queueBuffer or connect consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); EXPECT_EQ(10, buffer->width); EXPECT_EQ(20, buffer->height); ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); // Transform hint is synced to producer but no auto prerotation consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); EXPECT_EQ(10, buffer->width); EXPECT_EQ(20, buffer->height); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); // Prerotation is driven by the consumer with the transform hint used by producer native_window_set_auto_prerotation(window.get(), true); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); EXPECT_EQ(20, buffer->width); EXPECT_EQ(10, buffer->height); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); // Turn off auto prerotaton native_window_set_auto_prerotation(window.get(), false); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); EXPECT_EQ(10, buffer->width); EXPECT_EQ(20, buffer->height); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); // Test auto prerotation bit is disabled after disconnect native_window_set_auto_prerotation(window.get(), true); native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270); native_window_set_buffers_dimensions(window.get(), 0, 0); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); EXPECT_EQ(10, buffer->width); EXPECT_EQ(20, buffer->height); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); } TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp mockConsumer(new MockConsumer); consumer->consumerConnect(mockConsumer, false); sp surface = new Surface(producer); sp window(surface); int count = -1; ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count)); EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count); consumer->setMaxBufferCount(10); ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU)); EXPECT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count)); EXPECT_EQ(10, count); ASSERT_EQ(NO_ERROR, native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count)); EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count); } TEST_F(SurfaceTest, BatchOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp cpuConsumer = new CpuConsumer(consumer, 1); sp surface = new Surface(producer); sp window(surface); sp listener = new StubProducerListener(); ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/false)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); std::vector buffers(BATCH_SIZE); // Batch dequeued buffers can be queued individually ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); for (size_t i = 0; i < BATCH_SIZE; i++) { ANativeWindowBuffer* buffer = buffers[i].buffer; int fence = buffers[i].fenceFd; ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); } // Batch dequeued buffers can be canceled individually ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); for (size_t i = 0; i < BATCH_SIZE; i++) { ANativeWindowBuffer* buffer = buffers[i].buffer; int fence = buffers[i].fenceFd; ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); } // Batch dequeued buffers can be batch cancelled ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); ASSERT_EQ(NO_ERROR, surface->cancelBuffers(buffers)); // Batch dequeued buffers can be batch queued ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); std::vector queuedBuffers(BATCH_SIZE); for (size_t i = 0; i < BATCH_SIZE; i++) { queuedBuffers[i].buffer = buffers[i].buffer; queuedBuffers[i].fenceFd = buffers[i].fenceFd; queuedBuffers[i].timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; } ASSERT_EQ(NO_ERROR, surface->queueBuffers(queuedBuffers)); ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } TEST_F(SurfaceTest, BatchIllegalOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp cpuConsumer = new CpuConsumer(consumer, 1); sp surface = new Surface(producer); sp window(surface); sp listener = new StubProducerListener(); ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/false)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); std::vector buffers(BATCH_SIZE); std::vector queuedBuffers(BATCH_SIZE); // Batch operations are invalid in shared buffer mode surface->setSharedBufferMode(true); ASSERT_EQ(INVALID_OPERATION, surface->dequeueBuffers(&buffers)); ASSERT_EQ(INVALID_OPERATION, surface->cancelBuffers(buffers)); ASSERT_EQ(INVALID_OPERATION, surface->queueBuffers(queuedBuffers)); surface->setSharedBufferMode(false); ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } } // namespace android